What Uncle Bob's "I Don't Read Code Anymore" Taught Me About Testing

작성자

카테고리:

← 피드로
DEV Community · Semion Leyn · 2026-07-28 개발(SW)

Semion Leyn

I’ve come across a post by Uncle Bob on X (link) (the author of Clean Code) that has a couple thousand reposts. It states that he does not read code anymore. Yep, the same person who wrote a book saying your code should be easy to read because developers read code much more frequently than they write it.

So what is Uncle Bob’s method? Instead of reading the code written by AI agents, he asks the agents to make tons of tests around this code.

Of course some people agree and some do not agree. If you don’t read the code, the meaning of your ownership becomes somewhat blurred. But if you read all the code, you lose most of the productivity. I can’t judge here or provide my “valuable” opinion. I decided to write this post (and I don’t write much) because of a recurring topic that comes up here and there.

A lot of discussion now is about validating AI. Of course, validation in biology and bioinformatics is not the same as validation in software engineering, but I feel there is some valuable overlap.

The thread and reposts mention different tests that programmers, including Uncle Bob, use to curb their agents. Diving in opened up a world of testing that turned out to be much bigger than I expected, as a bioinformatician.

Correctness

  • Unit tests – Check individual functions or small units of code in isolation. They check logic, error handling, and edge cases. The most well-known type of software test.
  • Integration tests – Check that separate modules work correctly together (e.g., your API talking to the database).
  • Functional tests – Verify the software does what it’s supposed to do from a black-box, requirements standpoint. You give the software input data and look at the output. For example, if you write a new SNV-calling pipeline, you input sequencing data against a reference and check whether the pipeline outputs known SNVs with the required sensitivity and specificity.
    Functional tests also have some common subtypes worth knowing:

    • Smoke tests – A quick check that the basic features work, e.g., running your software with default parameters.
    • Regression tests – Check that a recent update did not break existing features.
    • User Acceptance Testing (UAT) – Checks whether real users confirm their needs are met. “Real users” can be community scientists downloading your tool from GitHub, or selected partners in an early-access or co-development program. Like the other categories here, UAT has its own sub-world: alpha testing, beta testing, validation of business value, validation that software meets contract criteria, and validation that regulatory requirements are met (GDPR, HIPAA, FDA). Note: despite the similar name, acceptance tests (below) are run by developers/QA, while UAT is run by real users in real-world use cases.
  • Acceptance tests / Gherkin tests – Confirm a full feature satisfies the business requirement it was built for. Gherkin tests are written in a structured, human-readable format (Given/When/Then) so non-engineers can review intended behavior without reading code: Given describes the context, When describes the specific user action or event captured by the software, Then describes the expected outcome.

  • Truth-set / golden-dataset testing – Not from Uncle Bob, but from my bioinformatics experience. In bioinformatics, and especially in clinical bioinformatics, it is hard to generate synthetic tests, for multiple reasons mostly related to what’s happening in the wet lab. So, biology relies a lot on ground-truth samples. It’s worth a separate post, but for NGS (my primary expertise) a good overview resource is the MDIC SRS Report: Somatic Variant Reference Samples for NGS. It’s from 2019 and maybe a little outdated — it definitely has a lot of dead links — but I’ve worked with the described samples pretty recently. It describes several classes of reference material:

    • Synthetic DNA – Synthetic DNA sequences with predefined genetic variants are added to highly characterized DNA.
    • Genomic DNA – DNA material with characterized variants.
    • Cell-free DNA – DNA material extracted from blood samples.
    • Human cell lines – Immortalized cell lines that have been extensively studied. Genome In A Bottle (GIAB) is probably the most famous, and can be used to assess your system on high-quality DNA.
    • Tissue / Formalin-fixed paraffin-embedded (FFPE) – A lot of clinical samples are preserved as FFPE, mainly for histological reasons. However, for NGS, the FFPE sample preparation process is highly deleterious, resulting in highly fragmented, damaged DNA. But due to the clinical importance of FFPE, a lot of bioinformatics NGS workflows should be tested on FFPE samples.
    • Orthogonal validation tests – One more of my bioinformatics inputs. When you don’t have a good, commercially available characterized sample, another way to validate is to check your output against two or more independent methods. For example: NGS, Sanger sequencing, long-read technologies, optical mapping, cytogenetics, etc.

Robustness Under Stress

  • Performance tests – Measure response times, throughput, and resource usage under expected or peak load, to catch regressions. This matters especially in cloud computing, where overestimating computing resources leads to overpayment and underestimating them can lead to a system crash. The subtypes I find most relevant as a bioinformatician:
    • Load testing – The simplest form of performance testing: how does the system (a bioinformatics pipeline, in my case) behave under normal load? For example, you expect your company to process 100 NGS samples a day, at around 10M read pairs per sample.
    • Stress testing – How would the system behave beyond normal load? What if one day you need to process 10,000 samples, or samples with 100M reads each?
  • Torture tests – Push the system to extreme, high-load, or edge-case conditions to find where it actually breaks. Would your pipeline break on a 100M-read sample? A 200M-read sample?

A word of caution on the cost of these tests (FinOps — one of the new terms I’ve picked up recently): in a cloud environment, testing too many scenarios can get expensive, and so can missing a stalled process that runs unnoticed for a long time. Local HPC users and DevOps teams can also get quite upset if you overload nodes and cause a long queue for other jobs.

Rigor Checks (Testing the Tests Themselves)

  • Mutation testing – Deliberately introduces small bugs into the code and checks whether the existing test suite catches them — for example, by replacing constant values, altering decision logic, or skipping code structures. If the tests still pass, the suite isn’t rigorous enough.
  • Test coverage – Measures what percentage of the codebase is actually exercised by tests, flagging untested areas. Of course, the quality of the tests matters more than the coverage number itself.
  • Reproducibility / determinism testing – This wasn’t in the threads, but I feel it’s important in both bioinformatics and AI. Using probabilistic approaches requires testing your input several times to find out whether the output is stable. AI/ML is mostly probabilistic in nature, and bioinformatics, even when it doesn’t use AI/ML explicitly, can involve sampling or other probabilistic techniques.

Another source of variation is switching a software or package version — results can change dramatically even with a minor version update. CLIA/CAP/CLEP regulations for clinical testing require re-evaluation on software changes.

Non-Functional and Systemic Checks

  • Security testing – Looks for vulnerabilities rather than functional correctness: is sensitive data secure, do user permissions work as expected, is the system protected from code injection?
  • Architecture checks – Automated verification that the code respects defined architectural boundaries and dependency rules: are all modules connected as intended, are naming conventions followed, is package usage consistent (e.g., you’re not using both os and pathlib in the same Python project for the same purpose), does your dependency graph avoid cycles?
  • Quality metrics – General code-health indicators (cyclomatic complexity, dependency structure, module size) that hint at maintainability without reading line-by-line. A few interesting ones:
    • Defect density – Number of bugs found per 1,000 lines of code.
    • Defect escape rate – Percentage of bugs found in production vs. testing.
    • Defect resolution time – Time required to fix a bug.

The DORA (DevOps Research and Assessment) group at Google has proposed a related but distinct set of metrics. While software-testing quality metrics try to predict software success before release, DORA metrics assess software delivery performance after code reaches production. DORA groups its metrics into throughput and stability:

Throughput

  • Lead time – Amount of time required to bring a committed change to production.
  • Deployment frequency – Number of production releases in a given time period.
  • Failed Deployment Recovery Time – A mirror of defect resolution time, but measured in production. To me, it straddles both throughput and stability.

Stability

  • Change failure rate – A sibling of defect escape rate: the rate of deployments/releases that require immediate intervention after reaching production.
  • Deployment rework rate – The share of production releases that were unplanned but triggered by production incidents.

Exploratory Correctness

  • Property tests – Instead of checking specific input/output pairs, define general invariants the code must always satisfy, then generate many varied inputs to try to break them.

To me, property tests feel similar to unit/integration/functional tests from the “correctness” section above. The main mindset shift is thinking about what properties your function should have, rather than what exact outputs it should produce. For example, say you’re testing a function that finds a DNA motif. In a unit test, you’d think through edge cases: DNA fragments that contain the motif, and fragments that don’t. In a property test, you’d define the invariant instead: if the input DNA contains the motif, the function returns its coordinate; if not, it returns -1. Then you generate random DNA/motif pairs following this rule and see how many times the function breaks.

Process and Human Layer

  • QA procedures – Broader, often manual, quality workflows that check the software from a user/product standpoint.
  • Generated artifacts review – Reviewing the docs, configs, and diagrams an agent produces around the code, as another proxy for correctness — a kind of sanity check for deliverables.

I haven’t used all of these tests, and I’m not even sure all of them belong in a typical bioinformatics pipeline — although bioinformatics is always trying to converge with software engineering practices. Still, I think being familiar with these concepts is very valuable for collaborating with both software teams and AI coding agents.

원문에서 계속 ↗

코멘트

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다