Agile, twenty-five years on
In 2001, seventeen people wrote one page. Its first line put individuals and interactions above processes and tools. A quarter of a century later, an industry sells processes and tools in that page’s name.
Sixteen hours of training make a Scrum Master. The title describes nothing anyone can check. It comes with a calendar of events, a wardrobe of hats and a vocabulary that impresses whoever has never shipped anything. The manifesto gets quoted because quoting it sounds intelligent. Almost nobody applies what it says, and nobody measures their own failure.
The real product of all this is diffuse responsibility. The process is followed, the boards are up to date, the velocity is stable, and the software is slow, late or broken. Nobody answers. The failure belongs to the method, or to the way the method was applied, and the cure is more method.
When everyone follows the process, nobody answers.
Its own authors said so. In 2014 Dave Thomas wrote that the word had “been subverted to the point where it is effectively meaningless”. In 2018 Ron Jeffries told developers to abandon it, and Martin Fowler named the Agile Industrial Complex. Jeffries again, on story points: “I may have invented story points, and if I did, I’m sorry now.”
Look at what ships instead. SQLite carries 590 times more test code than code. Linux delivers a major release every two or three months. Basecamp works in six-week cycles of its own design and threw the backlog away. About eight people around Jonathan Blow finished The Witness. OpenCode has passed 22,000 commits in the open. Add almost all the open-source software your business relies on. None of these people made their name as an agile guru. In each case someone answers for the direction, and the work is judged by what ships.
Agile is no fringe experiment. In the industry’s own survey, 71% of respondents use it in their software lifecycle, 63% of them run Scrum, and 11% are very satisfied. It is the default, so it owns the results. A 1983 Apple IIe answers a key press in 30 milliseconds; a 2014 PC with hundreds of thousands of times more transistors takes 50. A 2020 review in Science multiplied two matrices in plain Python: 7 hours, 0.0006% of what the machine could do. The choice of language alone gave a factor of 47, and code engineered for the hardware finished in 0.41 seconds, over 60,000 times faster. Casey Muratori measured a factor of 10 or more between “clean code” and plain code on one simple problem. Computers got faster, and bad software spent the gain, with few new uses to show for it. The median mobile web page went from 505 KB in 2014 to 2,559 KB in 2025. In 2022 CISQ put the cost of poor software quality at 2.41 trillion dollars for the United States alone. In July 2024 one faulty update crashed about 8.5 million Windows machines.
Software is buggy and unreliable, the web above all, and most teams are in denial about it. Two things feed this. Teams reach for tools more complicated than their problem, tools they cannot operate reliably. And competence has thinned out: Jonathan Blow warns that the knowledge of how to build things is being lost, and David Graeber gave the box tickers and the taskmasters their names. Agile gave them a calendar.
Agile rarely produces anything good, and when something good comes out, it is in spite of agile. Individuals with drive and ambition made it so, not committees there to collect a paycheck, look busy and stay inside the guard rails. Nobody ever got a transformer paper out of a sprint planning. The trance has lasted twenty-five years. It can stop.
There are good ideas in agile: small increments, continuous integration, fast feedback, an ordered backlog. They are engineering, and they stay. The rest goes: the rituals, the hats, the sprints, the points. A team should stand on what can be checked. A file in a repository, a gate in a pipeline, a number on a graph.
Sources Manifesto for Agile Software Development · The Scrum Guide · Scrum Alliance, Certified ScrumMaster · Thomas, Time to Kill Agile · Jeffries, Developers Should Abandon Agile · Jeffries, Story Points Revisited · Fowler, The State of Agile Software in 2018 · How SQLite Is Tested · How the Linux development process works · Shape Up, Bets, Not Backlogs · The Witness · OpenCode · 17th State of Agile Report · Luu, Computer latency: 1977-2017 · Leiserson et al., There’s plenty of room at the Top, Science, 2020 · Muratori, Clean Code, Horrible Performance · CISQ, The Cost of Poor Software Quality in the US · 2024 CrowdStrike outage · Web Almanac 2025, Page Weight · Blow, Preventing the Collapse of Civilization · Graeber, Bullshit Jobs
I
People answer.
Good faith
All of this assumes people who want the product to be good. If you are surrounded by people who want to do nothing, no methodology will save you, this one included. You have a graver problem, and it comes first.
It also assumes people who know their trade. Numbers inform a judgement and never replace it. No process teaches someone what good work looks like.
Most management machinery rests on the opposite assumption. McGregor described it in 1960: people dislike work and must be supervised into doing it. Timesheets, daily reporting and burndown charts are that assumption with a modern interface. Treating people as economic units from which value must be extracted is ridiculous, and it produces the behaviour it fears.
A method that makes people prove they are working assumes they would not otherwise. Hire people who would, and the method has little left to do.
Sources McGregor, Theory X and Theory Y (The Human Side of Enterprise, 1960) · Principles behind the Agile Manifesto: “Build projects around motivated individuals… and trust them to get the job done.”
No ceremony
A ceremony is a meeting you attend because of what time it is. The word is honest: it describes a belief, and a dogma to observe. Nobody needs faith to work. A free and intelligent person knows when something needs doing, does it, and calls on whoever they need at that moment.
Nobody needs a ritual to know when to work.
- A meeting is an invitation. One subject, the people it concerns, an outcome written down afterwards. They accept or they decline. Nobody comes to pretend.
- Writing comes first. A status is readable in the repository and the tracker. A decision is written where it applies. Time together is for what writing cannot do: arguing, designing, unblocking.
- Anyone can ask anyone for help. No slot, no intermediary.
- The retrospective is continuous. Whoever sees something to improve puts it in the backlog, carries it, and raises its priority by writing why. Nobody stops the whole team once a fortnight to collect sticky notes, and nobody asks for permission.
- Focus is protected. Software is made in long stretches of uninterrupted thought. One meeting in the middle of an afternoon costs the whole afternoon.
Each person is a productive actor with a say in the work. An army runs on orders. A team of engineers runs on people who see what has to be done.
Sources The Scrum Guide: the events of a sprint · Graham, Maker’s Schedule, Manager’s Schedule
One change, one author
Every change has an author, and the author answers for it, until it runs in production and after. When a release degrades something, the person who made the change works out why and fixes it. They have the context. That is what responsibility means here, and it has nothing to do with blame.
Finding a culprit has no use, and it teaches people to hide problems. The aim is to fix, to understand, and to make the failure impossible next time: a test, a gate, a safer deployment, a better measure, an alert that would have fired. The post-mortem is a file committed in the repository concerned, with a timeline, the causes and the actions, each action with its task number.
Responsibility needs the matching power. Someone who answers for a change can read the logs, reach production through the normal path, roll back, and decide. Holding people responsible for what they cannot touch is theatre.
One author. One answer. No culprit.
Sources Google SRE, Postmortem Culture: Learning from Failure
Roles with plain names
Scrum Master, Product Owner, Agile Coach, Release Train Engineer: hats you need a course to understand. A role needs a plain word and one sentence on what it answers for.
| Role | Answers for |
|---|---|
| Captain | The course. It is their project: what gets built, what gets dropped, the final word when the others disagree. Faces the clients and the management, so that the team does not have to. One person, because a final decision cannot be shared. |
| Coordinator | Keeps the place tidy: a backlog and tracking tools that are clean and true, what is blocked and by whom, who needs to talk to whom. An assistant to the team. A deputy project manager is exactly what this role is not. A large team has several, because communication bandwidth is what runs out first. |
| Lead dev | The technical coherence of the product: architecture, conventions, the decisions that bind the future. Says no, with proof. |
| Developer | Their changes, from the first commit to production. A full participant in every technical decision. |
| Operator | The run: CI, deployments, environments, monitoring, on-call, backups. Automates themselves out of each manual step. |
| Domain expert | What must be produced, written in technical detail: rules, edge cases, examples that can become tests. Has the technical skill to do it. A vague functional description is not a specification, and someone who can only write those is not hired. |
| Tester | What the author cannot see: exploration, unexpected use, the test strategy as a whole. The role teams forget first and miss most. |
| Designer | When the product has users: what they see and how they get through it, accessibility included. |
All of this is for teams of ten to thirty people. A smaller team stacks several roles on one person, a larger one gives each role its own people, and what each role answers for stays the same. A duo needs none of it.
People are autonomous in how they work. A developer who raises a priority and writes why wins over the order of the backlog. The course is the captain’s: they listen, they decide, and they answer for the direction.
If you cannot say what someone answers for, the role does not exist.
Sources The Scrum Guide · WCAG 2.2
A tech lead who can say no
Bad technical decisions survive because nobody is in a position to stop them. A strong tech lead is someone who can say: no, this is wrong. That takes no responsibility away from the other developers. It opens a precise technical exchange, with proof on the table: a benchmark, a prototype, a failure mode, a reference. Nobody in that exchange is afraid of breaking things.
Too many projects drag along a system their predecessors left inadequate, or simply unfinished. A homemade framework, a half-done migration, two ways of doing everything. Out of laziness or false urgency, everyone agrees to carry it a little longer. It is a ball and chain on the ankle, and the interest is paid on every change. Deleting code is work with a value. Finishing a migration before starting the next one is a rule.
When the numbers speak, the decision is easy.
- Several developers complain about the same thing? That is no longer a subject for discussion. They work on the product full time, they have a say, and they are autonomous: if something needs doing, they do it and integrate it.
- A real disagreement is settled by measuring. Write the experiment, run it, keep the result in the repository. Seniority and volume are not arguments.
- Who designs it works on it. Design happens among the people in this code every day, on concrete facts: this value is not available in that context, that change breaks this job. A rule from a book knows none of that, so it only breaks ties. Whoever draws the design answers for what ships.
- Prefer the boring choice. Every new technology costs attention for its whole life. A team can afford very few of them, so spend them on what makes the product different.
- Reversible decisions are taken fast. Irreversible ones, a public API, a data model, a vendor, get the time and the written record they deserve. Once people depend on a behaviour, it is part of the contract, documented or not.
Sources Fowler, Technical Debt · McKinley, Choose Boring Technology · Hyrum’s Law · Goedecke, You can’t design software you don’t work on · Bell, Don’t Feed the Thought Leaders
The war on Conway
“Any organization that designs a system will produce a design whose structure is a copy of the organization’s communication structure.”Melvin Conway, 1968
This law takes over in silence. You split the teams for reasons of budget or hierarchy, and two years later the software has the same seams, the same walls and the same queue at every border. A front-end team and a back-end team give you an API shaped like a meeting. Three vendors give you three systems and a data-sync job nobody owns. Nobody decided that. Everybody suffers it.
It cannot be repealed. It can be made conscious. Every organisational split has a consequence for the software, and the consequence can be read in advance: if we separate these two teams, we will never change this without that, and we know we will need to. Said out loud, the sentence changes the decision. You take the other split, or you arrange things differently. Used on purpose, the law works for you: draw the teams around the architecture you want. Thoughtworks calls that the inverse Conway manoeuvre.
Drawing the teams is drawing the system.
- Keep close what changes together. One repository or several, nobody has to adopt a monorepo. Distance between things that move as one is what costs.
- Teams own a whole slice of the product, from the data to the screen, and can ship it without asking another team. A change that needs three teams is a design flaw.
- No isolated teams around interdependent work. Each modification turns into an endless chain of slow conversations, the run becomes a chore and everything crawls. Adding people to that makes it worse: communication paths grow faster than headcount.
- Think in domains. Name things as the business names them. Draw boundaries around meaning, not around technical layers. Modules at the scale of a modular monolith suit most products long before microservices do.
Sources Conway’s Law and the 1968 paper · Fowler, Conway’s Law · Thoughtworks, Inverse Conway Maneuver · Team Topologies · Brooks’s law · Fowler, Domain-Driven Design · Fowler, Monolith First
Nothing lives in one head
Knowledge belongs in the repository, in the tests and in the habit of reviewing each other’s work.
- A newcomer ships in their first days. The README, the one-command setup and CODEOWNERS are enough to start. Every question they had to ask out loud is a bug in the documentation, and they fix it.
- Review spreads knowledge as much as it catches bugs. Small changes get real reviews. A two-thousand-line pull request gets a thumbs-up.
- Beginners make mistakes. Give them work at the scale of what they know, then a little more. That is mentoring, and a team that cannot do it cannot do much else.
- Competence is a daily practice. Reading the sources of your dependencies, the standards, the post-mortems of others. Time for that is part of the job.
- Developers are not spare parts. The people who build a program hold its theory: why it is this way, what each change would break. Code and documents carry part of it. Naur wrote in 1985 that the notion of the programmer as an easily replaceable component has to be abandoned, and that a program dies when its team is dissolved. Newcomers learn the theory by working beside the people who have it.
- Hire for the work. Have candidates read real code, find a real bug, write a real specification. A trivia quiz measures a trivia quiz.
A system one person understands is an incident waiting for a holiday.
Sources Google Engineering Practices, Small CLs · Naur, Programming as Theory Building (1985)
II
The repository is the truth.
One source of truth
Code first. What the repository holds is true. A ticket, a wiki page, a dashboard or a slide that says otherwise is wrong.
Generate everything that can be generated: API references from the schemas, schemas from the types or the reverse, diagrams from text, infrastructure from code, changelogs from commits or fragments, client libraries from contracts. Writing the same truth by hand in a second place is double maintenance, and double maintenance ends as ambiguity in the system. The CI checks that what is generated is up to date.
When they disagree, the repository wins.
What cannot be generated is the why. Why this database, why not that library, what was tried and failed. That part is written by hand, short, in the repository, next to what it concerns: a decision record, or src/md. It matters even more in the age of AI, because intent now lives in prompts, and prompts evaporate. Keep what a prompt decided as Markdown in src/md, and point to it from the commit. Leave the raw transcript out.
Documents live in docs/, beside the code: technical, functional, architectural, operational. They change in the same commit as what they describe, and they go through review. Stop marinating them in second-rate external tools. Open formats, Markdown, Mermaid, plain text, SVG, CSV, cover nearly every need. They are free, they diff, they export anywhere, they outlive vendors, and machines and people read them equally well. Fortunes go into tools that slow teams down.
Tools stay light. Each does one job, has a command line or an API, and can be replaced. Never adopt a tool where everything has to go through a visual dashboard: what needs a mouse cannot be scripted, reviewed or versioned.
Tidiness is not a detail for maniacs. Users only see the finished product, but the product is an output of the repository: of its code and of how it is organised. A badly kept repository is a bad input. Keep the boring layout everybody recognises.
project/ README.md what it is, how to run it CONTRIBUTING.md how to change it CHANGELOG.md what changed, for people SECURITY.md how to report a flaw THREAT_MODEL.md what you defend, and against what LICENSE what you may do with it CODEOWNERS who to ask .editorconfig formatting, settled .gitattributes line endings, settled src/ md/ the intent, next to the code docs/ technical, functional, architecture, runbooks tests/ fixtures/ test data, small and versioned scripts/ the one-command tasks deploy/ infrastructure as code containers/ how it is packaged .github/ or your host's equivalent: the CI, in code
Sources src/md · Docs as code, Write the Docs · Diátaxis · The C4 model · Nygard, Documenting Architecture Decisions and ADR · Mermaid
Standards first
Most of these decisions were taken long ago, by everybody. We already have the answer. Do it, and move on to something else. Keep your debates for your own problems. A costly decision taken when the standard way was one web search away has no excuse, and the earlier it is settled the less it costs.
Never turn into a debate what the world has already approved.
| Subject | The answer, already |
|---|---|
| Project files | README, CONTRIBUTING, CODEOWNERS, LICENSE with SPDX identifiers, SECURITY.md |
| History | Conventional Commits, with the task number. Keep a Changelog |
| Versions | Semantic or calendar, your pick. Written down, every release tagged, verified by the CI |
| Style | .editorconfig, the language’s own formatter and linter, run by the machine. Formatting is never discussed again |
| Interfaces | OpenAPI, JSON Schema, Problem Details for HTTP errors. A schema wherever one exists |
| Data | UTF-8. UTC and ISO 8601 for dates. Open formats. Versioned migrations |
| Packaging | OCI images, the platform’s native package, lock files |
| Telemetry | OpenTelemetry |
| Configuration | The Twelve-Factor App: configuration in the environment, one build for every environment |
| Security | security.txt, the OWASP lists, the platform’s own cryptography. Nobody writes their own |
| Accessibility | WCAG 2.2 |
This asks for rigour: knowing the standards, having absorbed them, keeping them up. Competence shows in how little of a project had to be invented.
Sources each line above links to its standard
Every action has a number
Every action gets a task number and enters the backlog. Even a one-off. Even a production fix at three in the morning. Even a change to a server setting. This is the one piece of bureaucracy worth holding sacred, because it costs a line and it answers, years later, the only question that matters about a change: why.
fix(api): cap the retries (PROJ-142)
No number, no commit.
The chain runs from the request to production: task, commit, review, build, release, deployment. From a line of code you reach the reason, and from a bug report you reach the code. The CI refuses the commit without a number.
The tracker itself hardly matters, and the choice deserves an hour. Any tool will do if it has an API or a command line, short numbers that are never reused, and an export to plain files. The commit outlives the tool. The backlog is an ordered list, and the order is the only thing in it that takes judgement.
Sources Conventional Commits, summary
One command
Clone, one command, it runs. Test: one command. Build, release, reset the local database: one command each, the same on every machine, and the same the CI calls. What the CI does and what a developer does must never be two different things.
- The environment is code. Pin the toolchain and the system dependencies: a dev container, Nix, a version file, whatever fits.
- Keep environments alike. Same services, same versions, same deployment path, from a laptop to production. Every difference is a place where bugs hide.
- Configuration lives outside the build. One artefact goes through every environment. What changes is configuration, declared and validated at start-up.
- Secrets never enter the history in clear. Encrypted in the repository or held in a vault, rotated, scanned at every commit. Once committed, a secret has leaked.
If it only works on your machine, the repository is broken.
Sources Twelve-Factor, Config and Dev/prod parity · Dev Containers · Nix · SOPS · Gitleaks
Tests are the specification
A specification nobody runs drifts from the product on the first day. Tests are the version that cannot lie. The domain expert’s examples become executable, and a requirement is done when its test passes.
- Many fast tests, few slow ones. Most checks at the level of the unit, fewer across components, a handful end to end. The whole suite runs on every change.
- A bug gets a test before it gets a fix. It fails, then it passes, and that bug is gone for good.
- Treat a flaky test like a production incident. Quarantine it, give it a number, fix it. A suite people have learned to rerun no longer protects anything.
- Test the borders. Contracts between services, migrations against real data shapes, behaviour under load, recovery after failure.
- Coverage shows what nothing checks. Chasing the percentage produces tests that check nothing.
If nobody runs the specification, it drifts.
Sources Vocke, The Practical Test Pyramid · Fowler, Eradicating Non-Determinism in Tests · Pact: contract tests
Rebuild it, byte for byte
A serious company can rebuild any version of its application, from any point in its history, identically, byte for byte. A customer reports a bug in a release from eighteen months ago: you produce that exact binary, from source, and you look at it. An auditor asks what ran in production on a given day: you can show it.
If you cannot rebuild it, you cannot vouch for it.
A build is reproducible when, given the same source code, the same build environment and the same build instructions, anyone can recreate bit-by-bit identical copies of the artifacts. Compare before signing: a signature changes the bytes. It is also a security property. In March 2024 a backdoor was discovered in xz, a compression library used across Linux. The modified file was not in the Git repository: it existed only in the release archives. What is published must come from the repository, and be checkable against it.
- Pin everything. The toolchain, every dependency in a lock file with its hash, the base images by digest.
- Keep your own copies. A registry that disappears must not make your history unbuildable.
- Remove what changes by itself. The clock, the order of files, the build path, the machine, the locale.
- Check it. Build twice in clean environments and compare the hashes. The CI refuses a release whose two builds differ.
An open-source side project is another story.
Sources Reproducible Builds, definition · Reproducible Builds · XZ Utils backdoor
Ship the proof
A release is more than a binary. Beside it, every time, as vital as the application itself:
- The changelog: what changed, for people.
- The software bill of materials: every component and version in the build, in an open format. When Log4Shell came out in 2021, companies spent days finding out whether they ran Log4j at all. With a bill of materials, “are we affected?” is a query.
- The provenance: which commit, which builder, which inputs.
- The signature: no obligation, a mark of quality and security. Who published this, and proof that nothing changed since. Keys live outside the repository, and a client verifies before it installs.
- The licences: what you ship, and under what terms.
Ship the proof next to the product.
Sources SPDX and CycloneDX · SLSA · Sigstore · OpenSSF Scorecard · Log4Shell
Know what you defend
A dependency scan covers one door. Security starts with four plain questions, answered in writing: what are we working on, what can go wrong, what are we going to do about it, did we do a good enough job. The answers fit in a THREAT_MODEL.md at the root of the repository, updated in the same commit as whatever changes them.
- Least privilege. Every person, service and CI job gets the rights it needs and nothing more. No build job holds the keys to production.
- Short-lived credentials, no shared accounts. Tokens expire. Everything that can reach production asks for strong authentication.
- Trust nothing at the border. Validate input, encode output, use the framework’s protections. The OWASP verification standard is a ready-made checklist.
- Leave a trail. Who did what in production, and when, kept out of reach of the people it records.
- Rehearse the leak. How do you rotate every secret, and how long does it take? That is a number too.
- Leave the door open to whoever finds a flaw.
SECURITY.md,security.txt, and an answer.
Write down what you defend, and against what.
Sources Threat Modeling Manifesto · OWASP Threat Modeling Cheat Sheet · STRIDE · OWASP ASVS · NIST SSDF · Principle of least privilege
III
Measurements judge.
Always deployable
More Kanban, less sprint. Work flows. It does not come in two-week crates. The trunk can ship at any moment, and the speed of the loop between an idea and its verification is one of the most important numbers a team has.
At any moment: deploy, test, update.
- Trunk-based development. One trunk, tags, feature branches that live hours or days, hotfix branches. Long-lived branches are merges you have chosen to make harder.
- Limit the work in progress. Time in the system grows with what is in the system. Finishing beats starting.
- Separate deploying from releasing. Unfinished work ships switched off behind a flag, and flags get removed when they have served.
- Deploy gradually, and know how to go back. Canary, blue-green, progressive rollout, automatic rollback on a bad signal. In August 2012 a software fault sent Knight Capital’s erroneous orders to the market for about 45 minutes and cost the firm some 440 million dollars. A deployment is the most dangerous thing most teams do, so make it boring.
- Give each pull request its own environment where you can afford it: the domain expert tries the change in place and says at once whether it goes in the right direction. Agile had this idea. It just defined it badly.
Sources Trunk Based Development · Fowler, Continuous Integration · Little’s law · Feature Toggles and OpenFeature · Canary Release · Blue-Green Deployment · Knight Capital Group · Forsgren, Humble, Kim, Accelerate
Gates that refuse
On subjects that are not up for debate, the conclusion is automatic. Security risks climb every year, and being sharp and current on your dependencies comes first. Here the rigour is close to dictatorial, and it belongs to the machine. What is automated needs no reminder, no meeting and no policeman. The CI refuses:
- TraceabilityA commit without a valid number or format. A missing changelog entry.
- CorrectnessA failing test, a type error, generated files out of date.
- StyleFormatter and linter findings.
- VulnerabilitiesKnown flaws of high or critical severity, in dependencies, images and code.
- FreshnessA dependency more than 90 days behind. Update bots keep this cheap.
- SecretsAnything that looks like a credential.
- LicencesA component whose licence you may not ship.
- BudgetsA regression in size, speed or memory beyond the agreed limit.
- ReproducibilityA release whose two builds differ.
Dictatorial about the gate. Held by a machine, never by a person.
There is always an escape hatch. In an emergency, the administrators of the tools can do what they want. The override is written: who, why, a task number, a catch-up task in the backlog. An emergency is no zone without a trace, and an unrelated dependency never holds an urgent fix hostage.
Nobody waits. An AI reviews every change first, instantly, and a human decides to merge. Automatic review and automatic results remove the critical paths where people queue for each other. The gates themselves are fast, or they get skipped. Slow CI gets a task number like any other bug.
Sources OWASP Top 10:2021, A06 · OSV · Renovate · Semgrep · Gitleaks · REUSE
Measure the system, never the person
Every product has numbers that tell the truth about it. A web service has tail latency, a game has frame time, a desktop application has start-up time and memory, a batch has duration, a mobile app has crash-free sessions and battery. Find yours. Start from what the user feels, then from what the team lives through, and put them on a graph everyone can read.
| Family | What to look at |
|---|---|
| Delivery | Lead time of a change, deployment frequency, time a change waits for review, mean and worst CI time, work in progress |
| Reliability | Availability against its objective, change failure rate, time to restore, incidents and their recurrence |
| Performance | Percentiles, never averages: p50, p95, p99, p99.9. Throughput, start-up, memory, binary or page weight, Core Web Vitals on the web |
| Quality | Defects found after release, crash rate, flaky tests, age of open bugs |
| Security | Time to patch, age of dependencies, open vulnerabilities by severity |
| Cost | Cost per request or per user, CI minutes, infrastructure against usage |
| Users | Task success, time to first result, support tickets by cause, what is really used |
The tail deserves attention. In a system that fans out, one slow component makes the whole request slow, so the rare case for one server becomes the common case for your users. An average hides exactly that.
Never measured: lines of code. Number of commits or pull requests. Hours, days present. Story points. A ranking of people. No personal metric, none. Nobody is here to be compared.
The only thing that counts is the quality of the finished product.
Sources DORA, the four keys · Forsgren, Humble, Kim, Accelerate · Dean, Barroso, The Tail at Scale · Web Vitals · Goodhart’s law
See inside
Logs, metrics and traces are part of the product, written with it and reviewed with it. When something breaks, the first question is answered by a graph, not by a phone call.
- Start with the four golden signals: latency, traffic, errors, saturation.
- Structured logs, one trace across the whole path. A request has an identifier from the first hop to the last.
- Alerts are code, in the repository. An alert fires on what users feel, and someone can act on it. An alert nobody acts on gets deleted.
- Every alert links to a runbook, in
docs/, and the runbook gets shorter as the fix gets automated. - Watch the product too: crash reports, real user timings, the errors nobody reports.
Without telemetry, you are guessing.
Sources Google SRE, Monitoring Distributed Systems · OpenTelemetry
Promises in numbers
“It must be fast and reliable” means nothing. “99.9 % of requests answered in under 300 ms over thirty days” means something, and everyone can read whether it is true. Write the objective down, in the repository, with the product side.
The gap between the objective and perfection is a budget. While it lasts, ship fast and take risks. When it is spent, reliability goes first until it recovers. The eternal argument between features and stability becomes a reading of a number. The same goes for budgets of page weight, memory, start-up time or cost: a limit agreed in advance, enforced by a gate.
Put a number on the promise, or do not make it.
Sources Google SRE, Service Level Objectives · Google SRE, Error Budget Policy
Data outlives code
Code can be rewritten. Customer data lost is lost. In January 2017, during an incident at GitLab, the process of finding and using the backups failed completely, and production data was lost for good.
- Restore on a schedule, automatically, and measure how long it takes. That number is your real recovery time.
- Migrations are versioned code, reviewed and tested against a realistic volume.
- Change schemas in steps: expand, migrate, contract. Old and new versions of the application run side by side during a deployment.
- Know what you hold. Where personal data lives, why, for how long, and how it gets deleted.
A backup nobody has restored is a hope.
Sources GitLab, Postmortem of database outage of January 31 · Evolutionary Database Design · Fowler, Parallel Change
Dates come from the past
Story points and velocity are abstract numbers invented to avoid saying a duration, and then converted into a duration anyway. Teams argue over a five and an eight, and the number predicts nothing.
Without experience, an estimate means nothing. With experience, it is a judgement: an objective, never a guarantee. When someone asks “when will it be ready?”, look at how long similar work really took, and answer with a range. The tracker and the commits already hold the data.
Estimate from what happened.
Deadlines are fine. Keep a final one, so that scope can adapt to it, and ideal delivery dates along the way. Then measure the gap between the ideal date and the real one. If you are always behind, your expectations are wrong. Readjust them. When a date is fixed, cut scope. Cutting quality only moves the bill.
Sources Reference class forecasting · Little’s law
Cost is a measurement
Money is a number like the others, and a team should read it. The cost of a licence is visible. The cost of the hours it wastes is not, and that one is usually larger. Tools get bought to reassure people who will never use them, and every developer pays the price on every working day.
- Count the waiting. Ten minutes of CI, twenty developers, five changes a day each, 220 working days: about 3,700 hours a year, roughly two full-time people watching a progress bar.
- Count the meetings. For a one-month sprint the Scrum Guide allows up to eight hours of planning, four of review and three of retrospective, plus fifteen minutes a day. That is about twenty hours per person per month, one hour in eight. A team of twelve can spend 240 hours a month on it before writing a line.
- Count a tool in full. The licence, the integration, the training, the hours it wastes, and what it will cost to leave. Per-seat pricing grows with the team. So do the wasted hours.
- Know the cost of running the product, per user or per request, on the same graph as the rest. Every resource has an owner. What is idle gets turned off.
- Prefer what you can leave. Open formats and standard interfaces keep a vendor from setting your price. Price the exit before you sign.
Price the hours, not the licence.
Sources FinOps Foundation · The Scrum Guide: the timeboxes
Software is alive
Code is a living thing, and living things get pruned. Every feature costs something for as long as it exists: tests, support, attack surface, attention. Adding gets celebrated and removing is rarely planned, so products sink under their own weight.
- Know what is used. The numbers say which features nobody opens.
- The captain decides what goes. Removal gets a number, like any other change.
- Remove in the open. Announce it, warn in the product, give a date, then do it. Somebody depends on every behaviour you ship.
- Clean up what nobody sees. Dead code, retired flags, unused dependencies, finished migrations.
- Replace gradually. Grow the new system around the old one and move piece by piece. Big rewrites are where projects go to die.
What is not used gets removed.
Sources Fowler, Strangler Fig · Hyrum’s Law
Change without fear
Times change quickly. Being reactive was the good idea inside agile, and it never meant making a mess every five seconds. It means platforms, tools and above all skills that let you evolve without the anxiety of breaking everything. Small changes, tests, gates, reproducible builds, rollback, observability: everything above exists for this. A team that trusts its safety net changes things. A team that does not stops touching anything, and the product starts to die.
AI belongs in that toolbox: to write, to review, to explore, to automate what used to be too tedious to automate. It stays optional, but a team that refuses to see what the existing tools can produce falls behind. It does not change who answers. What an agent produces goes through the same gates, with the same author.
A product that works, and a team that can still change it next year.
Sources Manifesto for Agile Software Development: “Responding to change”
Order of adoption
Where to start
TL;DR
How to make software
- Hire people who know their trade and want the product to be good. No method replaces that.
- No ceremonies. Meetings are invitations, and improvement is continuous.
- Every change has an author who answers for it. Nobody hunts a culprit.
- One captain sets the course. Every role is a plain word and one sentence on what it answers for.
- A tech lead says no, with proof. Numbers settle arguments.
- The way you split teams becomes the shape of the software. Decide it on purpose.
- A system one person understands is an incident waiting for a holiday.
- What the repository says is true. Generate everything except the why.
- Never debate what the world has already standardised.
- No task number, no commit.
- Clone, one command, it runs. The same command as the CI.
- Tests are the specification. Treat a flaky test like an incident.
- Any version can be rebuilt, byte for byte.
- Every release ships with its changelog, SBOM, provenance and, ideally, a signature.
- Write down what you defend. Least privilege everywhere.
- The trunk can ship at any moment. Deployment is boring and reversible.
- What is not up for debate is refused by the CI, with a written escape hatch.
- Measure the product and the system. Never a person.
- Logs, metrics, traces and alerts are part of the product.
- Put a number on every promise.
- A backup nobody has restored is a hope.
- Estimate from how long similar work really took.
- Price the hours, not the licence.
- Software is alive. What is not used gets removed.
- Use every tool that helps, AI included. The author still answers.