All writing05 / 11Testing

Arguing Against My Own Code

After a year and a half of trying to break other people's software, the real change in becoming a developer is learning to doubt my own.

Zahin Mohammad

Software Developer

Published
Reading time
3 min read
Length
539 words
Topics
Testing, Engineering, Career

In July I moved from quality assurance to development, after a year and a half of trying to break other people’s software. On paper it’s a technical step: bigger features, more of the codebase, decisions I used to only comment on. The bigger change is a change of stance. For a year and a half, my job was to doubt code. Now I’m the one who has to believe in it long enough to ship it.

Builders Collect White Swans

There’s an old argument in the philosophy of science about swans. No number of white swans proves that all swans are white; a single black one proves they aren’t. Karl Popper built a theory of science on that asymmetry: you can never verify a general claim, only fail to falsify it.

Code is a general claim. “This function returns the right price” is a statement about every possible input. When you build something, the natural instinct is to collect white swans. You try the case you had in mind, and it works. You try a similar one, and it works. Each success feels like evidence. Mostly it’s repetition.

Testers are trained in the opposite direction. The whole job is the search for one black swan, and a passing test is only interesting for what it failed to find.

Holding Both Arguments

Good engineering seems to need both stances in one head: the builder who believes the design will work, and the tester who assumes it won’t and goes looking for where. Believe too little and nothing ships. Believe too much and you ship the bug that one honest question would have caught.

The tester’s voice isn’t mysterious. It asks a short list of very concrete questions, the same ones a good bug report usually starts from:

  • What if it’s empty? No items, no results, a blank string, a user with no permissions yet.
  • What if there’s exactly one, or far too many?
  • What if it happens twice? Double submits, retries, two tabs.
  • What if it’s slow, or fails halfway through?
  • What if the data is strange? Unicode names, negative amounts, a date near midnight in another time zone.

Write the Test That Would Embarrass You

The habit worth carrying from QA into development is writing tests that argue against the code instead of for it. Not “it adds an item to the cart”, which the code was written to do, but the inputs a skeptic would try first:

tests/Feature/CartTest.php
it('rejects quantities that make no sense', function (mixed $quantity) {
    $this->postJson('/api/cart/items', ['product_id' => 1, 'quantity' => $quantity])
        ->assertUnprocessable();
})->with([0, -1, 1.5, '3 apples', null, 10_001]);

Each value in that list is a small accusation. If the code survives all of them, the white swans start to mean something.

Doubt Is a Design Tool

The surprise is how early doubt pays off. A tester only gets to doubt finished software. A developer can doubt a design before a line of it exists, when the fix costs a sentence instead of a week. Asking “what if this happens twice?” while sketching an endpoint is how idempotency gets designed in instead of patched on.

So leaving QA didn’t mean leaving that voice behind. It meant moving it earlier, to the point where it’s cheapest to listen.

Next post06 / 11Spreadsheets Are Where the Real Requirements LiveWriting imports for twelve kinds of records showed me the business as it actually runs, which is never quite the business in the spec.

Open to select freelance work

Have an IdeaWorth Building?

Building a product, or hiring for one? I’d like to hear about it.

Based in Chittagong, Bangladesh. It’s here (GMT+6).