Drovenio Software Development Tips for Fewer Bugs
The export button worked perfectly in the demo. Then someone on a slow connection clicked it twice and got two copies of the same report. To the person using it, the first click looked like it had done nothing. To the server, both clicks looked like perfectly reasonable requests.
The bug looks obvious once you’ve watched it happen. Before then, the person writing the ticket, the person building the feature, and the person testing it might each picture a different outcome. Getting them to agree on what the second click should do is a small conversation that can save a morning of debugging later.
Table of Contents
- Decide what a second click should do
- Build one path all the way through
- Put the rule where people can find it
- Test the click that caused the bug
- Give reviewers a place to start
- Measure where the time goes
- Make the release easy to undo
- Watch one real workflow after deployment
Decide what a second click should do
A ticket that says “Add CSV export” gives you somewhere to start. The team still has to agree on what a useful export looks like. Does it include the current filters? Does it cover the full set of results or just the page you’re looking at? What should the customer see while the file is being prepared?
Write down one ordinary example and one awkward one. If Maya filters her orders to April, her CSV should contain all the April orders that match her filters, even if she can see only 25 at a time. If she clicks twice while that export is running, she should see the status of the job already in progress. Once it’s ready, she can request a fresh copy.
The same approach works for a small settings form. A line like “If saving fails, leave the edited fields on the screen so the user can try again” gives a developer and a tester the same thing to check.
Build one path all the way through
When a change touches several parts of an app, you can spend days finishing the database work and building the screen in parallel. Then the pieces meet and you find out the browser expects a file right away while the server plans to put the job in a queue.
I like to get one path working early. A test account requests an export for a single date range, a worker creates the CSV, and the user downloads it. Working through that sequence helps you spot which details need to travel with the job and what to show on screen while it runs.
Keep that first change small enough to describe in one sentence. If you’ve also found three files you want to tidy, give that cleanup its own change. A reviewer can then follow the new behaviour from click to response without having to work out which edits are cosmetic.
Put the rule where people can find it
Say customers can export up to 90 days at once. A comparison tucked between three date conversions makes the limit easy to miss. A function named isExportRangeAllowed gives readers a place to look. Have the server enforce the limit and make sure the form explains it in the same terms.
A small comment earns its place when there’s a reason the code alone can’t tell you. If larger exports used to tie up the reporting workers, say so next to the limit. Somebody will get a request to raise it eventually. Knowing why it’s there gives them a specific thing to measure before they change the number.
The same care helps when you name data. A field called requestedAt tells you when someone asked for a report. Pair it with completedAt and you’ve got a way to answer support when they ask how long it took for the file to become available.
Test the click that caused the bug
If a customer reports duplicate exports, start by writing down what they did. The useful detail might be that their browser timed out after the server had already accepted the request. A test that just calls the button handler twice can miss that gap.
Give each intended export its own request ID and reuse it when the browser retries. Then write a test that reaches the part of the app that creates jobs. Send two copies of the request with the same ID and check that both responses point to the same export and the system queued one job. If the requests overlapped when the bug happened, make them overlap in your test.
Other rules are quicker to check on their own. A small test can verify that a 90-day range passes and a 91-day range fails. Save the full browser test for a flow you want to verify from start to finish, such as selecting filters and opening the downloaded file. When one of those tests fails, you’ll have a good idea where to look.
Give reviewers a place to start
My favourite pull request descriptions tell me what to try first. For the export fix, a short instruction would do. “Simulate a slow connection, click Export twice, and check that the status page shows one job.”
Point out the decision you’re least certain about, too. What if the first export finishes just before the retry arrives? A reviewer who knows to look at that handoff can check whether the app still finds the original job by request ID. Give them a way to try a deliberate new export as well. That should get a new request ID, even when the filters match.
Pay attention if the description takes three paragraphs before you can say what a customer will see. You might have two independent changes in the same pull request. Splitting them gives each change its own test and makes it easier to see what caused a regression later.
Measure where the time goes
A slow export looks like one problem from the browser. Behind the scenes, the job might be waiting for a worker, pulling records from the database, or writing the file. Time each stage for a few representative exports before you pick a fix. Include a large account in your test data. Twenty rows on a laptop will tell you very little about an account with five years of orders.
If most of the wait happens before the job starts, look at how work gets queued and how many jobs the workers are handling. If the delay grows during the database query, look at how the query runs with a realistically sized set of records.
Now run the same exports again after the change. Keep the filters and test data fixed, then look at the slow runs as well as the typical ones.
A smaller average feels less impressive when customers with big accounts still wait just as long.
Make the release easy to undo
If you’re replacing the export screen, you could put it behind a feature flag, a switch that lets you turn it on for a small group first. Pick a signal you’ll watch, such as the share of exports that fail to finish within the usual time. Agree on what would make you turn the feature off and who’ll make that call.
Database changes need a little more planning. Say the new code stores an export’s status in a new column. Add a column that allows empty values and check that the old app can still read and write records. Then deploy the new code with a fallback for rows that have no status. If you have to roll back the app, the old version can keep working with the database.
Try that rollback in a test environment with jobs already in progress. It’s a good way to find out whether the old worker can finish jobs created by the new release while you still have time to adjust it.
Watch one real workflow after deployment
After deploying, try the workflow with a test account in production. For an export, select filters, request a file, and open it to check the rows. Then look at what’s happening for the people who have the feature. Are exports finishing at about the same rate as before? Is the queue growing faster than usual?
It helps to record a few events you can tie together with the same job ID. An accepted request tells you somebody asked for a file. A completion event tells you the file was ready to download. If someone reports a missing export, you can follow that one job and see which stage it reached.
The next time someone on a slow connection clicks Export twice, they should see one job and be able to get on with their day. Pick a feature you’re working on this week and ask what a reasonable person might do when it’s slow or confusing. That answer is often the best test you haven’t written yet.
