Practices#
The day-to-day habits that distinguish high-performing engineering teams. Methodology sets the cadence; practices set the quality.
Code Review#
The single most leveraged practice in software engineering. Every change reviewed by another engineer before merge; the discipline below is what separates code review from performative gate-keeping. What good review looks like.
Small PRs, single concern, ~hundreds of lines max.
Self-review first, the author reads their own diff before asking for review.
Specific feedback, “this query is missing an index for the
user_idfilter” beats “this might be slow”.Suggestions, not commands, author is responsible; reviewer consults.
Small turnaround, review within hours, not days. Long-pending PRs rot.
Approve when ready, block when blocking, no passive-aggressive “looks good but I have concerns”.
What good review isn’t:
Style policing for things that should be automated.
Personality-based.
Used to relitigate decisions that were already made elsewhere.
Pair Programming#
Two engineers, one task, one or two screens. Pairing trades headcount-hours for higher-quality output and faster knowledge transfer; it’s expensive on simple work and underpriced on tricky changes, debugging, or onboarding new engineers.
Driver / Navigator, one types, one thinks ahead. Switch often.
Pings, the navigator catches typos and broken logic in real time.
Knowledge transfer is the long-tail benefit; experienced + junior pairings lift everyone.
Costly in headcount-hours; valuable on tricky changes, knowledge transfer, debugging.
Async pair-programming via shared editors (VS Code Live Share, Zed multiplayer, JetBrains Code With Me) makes this practical for distributed teams.
Mob Programming#
Whole team works on the same thing on one screen. Roles rotate every few minutes. The technique scales pair programming up to the team level, with the same tradeoffs amplified, including both the high knowledge transfer and the limited team size it works for.
High knowledge transfer.
Fewer code reviews needed.
Hard to scale beyond ~4 people.
Best for cross-cutting work or kicking off a new system.
Test-Driven Development (TDD)#
Write a failing test, make it pass, refactor. Repeat. The discipline forces design choices when they’re cheap, leaves tests behind by construction, and gives you the safety net to refactor freely later, in domains where it actually fits.
Red, write a small failing test.
Green, make it pass with the simplest change.
Refactor, improve design while staying green.
Strengths.
Forces design choices when they’re cheap.
Tests exist by construction.
Confidence to refactor.
Pitfalls.
Tests coupled to implementation make refactoring harder, not easier.
Strict TDD on UI / async / external-system code is awkward.
“We do TDD” said by teams that don’t, harms credibility.
Behavior-Driven Development (BDD)#
Tests as natural-language specifications, often via Gherkin
(Given / When / Then). The technique is most useful when
business stakeholders and engineers genuinely co-author
scenarios; engineers writing Gherkin alone is the standard
anti-pattern.
Feature: Checkout
Scenario: Successful purchase
Given a customer with $100 in cart
When they pay with a valid card
Then the order is marked paid
And an invoice is sent
Tools: Cucumber, SpecFlow, Behave, pytest-bdd.
Useful when business stakeholders and engineers genuinely co-author tests. A common anti-pattern is engineers writing Gherkin alone; you’ve added indirection without the collaboration that justified it.
Domain-Driven Design (DDD)#
A modeling philosophy: focus on the business domain; let the model drive the code. Worth applying selectively to the parts of a system where domain complexity is real; forcing DDD on trivial CRUD is the standard over-engineering. Core concepts.
Ubiquitous language, business and engineers use the same terms.
Bounded contexts, subdomains with their own model.
Aggregates, consistency boundaries; one transaction per aggregate.
Domain events, business-meaningful occurrences as first-class.
Strategic vs. tactical patterns, macro design vs. micro idioms.
Worth applying selectively to the parts of the system where domain complexity is real.
Refactoring#
Improve design without changing behavior. Continuously, in small steps. The discipline below is what makes refactoring safe; atomic changes, test coverage, and a rule for when to extract instead of duplicate.
Rule of three, the third time you copy something, extract it.
Boy-scout rule, leave the code cleaner than you found it.
Atomic refactors, one transformation per commit.
Backed by tests, tests catch the “unchanged behavior” claim.
See Refactoring for smells, the core catalog, mechanics, large-scale strategies, and tooling.
Continuous Integration / Delivery#
The four practices that turn “we have CI” into a force multiplier. Trunk-based development, a green main branch, pipeline-as-code, and pipelines that run fast enough that engineers actually wait for them rather than ignoring them.
Trunk-based development, merge to main frequently.
Green main, the main branch is always shippable.
Pipeline-as-code, in the repo, reviewed like code.
Fast feedback, if CI takes 30 minutes, developers learn to ignore it. Aim for <10.
See CI / CD.
Documentation#
The five documentation artifacts every healthy codebase maintains. Documentation written while building is far better than documentation attempted afterward; treat it as part of the change rather than a follow-up task.
README, what is this and how do I run it locally.
Architecture decision records (ADRs), one short doc per significant decision.
RFCs / design docs, before significant changes.
Runbooks, per service, kept fresh.
Onboarding docs, updated by the most recent hire.
Documentation written while building is far better than documentation attempted afterward. Treat it as part of the change.
Knowledge Transfer#
The practices that keep critical knowledge from concentrating in a single person. Bus-factor risk is real; a sudden departure or illness shouldn’t be able to take a service down by accident. The four habits below distribute knowledge as a matter of routine.
Pairing rotations so no one owns a single area exclusively.
Bus-factor assessments, list the things only one person knows; fix.
Show-and-tell sessions, a 15-minute walkthrough beats reading cold.
Recorded sessions for distributed teams.
Retrospectives#
Honest reflection on a recent period. The format barely matters; what matters is that the team produces action items with owners and due dates by the end. A retro that produces nothing actionable is theater. Common formats.
Start / Stop / Continue, simple and effective.
4Ls, Liked / Learned / Lacked / Longed for.
Sailboat, wind / anchor / rocks / island.
The point is action items with owners and due dates. A retro that produces nothing actionable is theater.
On-Call#
The discipline that connects engineering decisions to operational reality. The team that wrote the code is the team that gets paged, which keeps short-term decisions honest about their long-term costs. The five practices below make on-call sustainable.
You build it, you run it, the team that wrote the code is the team that gets paged.
Sustainable rotation, typically a week per N engineers; pay or comp time off.
Runbooks for every alert, if the alert fires, the runbook tells you what to do.
Postmortems are blameless and produce action items.
On-call workload tracked, if it’s miserable, the team should be fixing the noisiest pages first, not adding more alerts.
Sustainable Pace#
Sprints aren’t sprints; they’re a sustainable jog forever. Burnout destroys velocity, quality, and people in that order. Vacation, real weekends, and reasonable workdays are infrastructure, not luxury.