Technical Decision Making
Senior engineers do not just implement decisions — they make them. The quality of your technical decisions is what separates a senior engineer from someone with senior years of experience.
Every line of code is a decision, but most are trivial and reversible. The decisions that define a senior engineer are the ones that shape the system for months or years: architecture choices, technology selections, build-vs-buy trade-offs, and the timing of debt repayment. Getting these right — or getting them wrong gracefully — is the core skill of senior engineering.
What Makes a Decision Technical
A technical decision is any choice that affects the system's architecture, reliability, performance, maintainability, or cost. Examples:
- Which database to use for a new feature
- Whether to build a service or use a third-party API
- How to handle backward compatibility during a migration
- When to take on technical debt and when to pay it down
- Whether to introduce a new programming language to the stack
- How to structure a service boundary
These decisions have long-lasting consequences. A bad choice in a code review is a bug. A bad architectural decision is months of rework.
The Cost of Bad Decisions
Consider a real-world example. A mid-stage SaaS company decided to use a document database for their core transactional data because it was trendy and the initial development was fast. Two years later, the company needed to add complex reporting features that required joins across multiple document collections. The queries were slow, the workarounds were fragile, and they eventually spent six months migrating to a relational database. The original decision was made in a single meeting with no written analysis.
The cost was not just the six-month migration. It was the two years of increasingly painful workarounds, the features they could not build, and the engineers who left because they were frustrated with the technical debt. One decision, one meeting, two years of consequences.
Decision Frameworks
Reversible vs. Irreversible
Not all decisions deserve the same rigor. Jeff Bezos famously distinguishes between "one-way door" and "two-way door" decisions:
- Reversible decisions (feature flags, API response format, library choice for an internal tool) — Decide fast. You can change it later. Spending a week debating a reversible choice costs more than making the wrong call and correcting it.
- Irreversible decisions (database choice for core data, public API contract, data model that clients depend on) — Slow down. Gather input. Write a design doc. These are the decisions you live with for years.
The skill is correctly classifying which type of decision you are facing. Most engineers err on the side of treating too many decisions as irreversible, which leads to analysis paralysis. A useful heuristic: if you can change the decision in less than a week of work, it is reversible.
Trade-Off Analysis
Every technical decision is a trade-off. Make the trade-off explicit:
| Option | Pros | Cons | Risk |
|---|---|---|---|
| Build in-house | Full control, exact fit | 3 months to build, ongoing maintenance | Team capacity |
| Use vendor X | Ships in 1 week | Monthly cost, limited customization | Vendor lock-in |
| Defer decision | No cost now | Problem grows, options narrow | Deadline pressure |
Writing this table forces clarity. Most bad decisions come from invisible trade-offs — costs that nobody acknowledged until they materialized.
A real-world application: a payments team needed to add fraud detection. The trade-off table looked like this:
Build custom ML model:
+ Tailored to our data, no per-transaction cost
- 4 months to build, need to hire ML engineer
Risk: We have no ML expertise in-house
Use Stripe Radar:
+ Ships in 1 week, proven system
- $0.02 per transaction, limited customization
Risk: Vendor dependency, cost scales linearly
Use open-source rules engine:
+ Free, customizable, ships in 2 weeks
- Manual rule maintenance, no ML capabilities
Risk: Sophisticated fraud bypasses static rules
The team chose Stripe Radar for launch with a plan to evaluate the custom model in 12 months if transaction volume made the per-transaction cost significant. Writing out the trade-offs made the decision obvious.
Decision Records (ADRs)
An Architecture Decision Record captures:
- Context — What is the situation? What constraints exist?
- Decision — What did we choose?
- Consequences — What are the expected outcomes, both positive and negative?
- Status — Proposed, accepted, deprecated, superseded.
A concrete ADR example:
# ADR-042: Use PostgreSQL for Order Data
## Status
Accepted
## Context
We need a data store for order records. Requirements include:
- ACID transactions for payment consistency
- Complex queries for reporting
- Expected volume: 10M orders/year initially, growing to 100M
The team has strong PostgreSQL experience. We evaluated
MongoDB, DynamoDB, and PostgreSQL.
## Decision
We will use PostgreSQL with table partitioning by month.
## Consequences
- Positive: Strong consistency, rich query capability, team expertise
- Positive: Partitioning handles growth to 100M orders
- Negative: Vertical scaling limits around 500M orders
- Negative: Schema migrations require more planning than schemaless
- We will revisit if order volume exceeds 200M/year
ADRs are not bureaucracy. They are the institutional memory that prevents your team from re-litigating the same decision every quarter. They also protect you — when someone asks "why did we do it this way?" six months later, the ADR answers the question.
The Decision-Making Process
For significant technical decisions, use a structured process:
Step 1: Frame the Problem
Before evaluating options, make sure you are solving the right problem. Write a one-paragraph problem statement. If you cannot, the problem is not yet clear enough to decide.
Bad framing: "We need a message queue." Good framing: "Our order processing pipeline is dropping events during traffic spikes because the synchronous API calls to the fulfillment service time out. We need to decouple order creation from fulfillment processing to handle 10x traffic spikes without data loss."
Step 2: Gather Input
For irreversible decisions, gather input from people with relevant expertise. This does not mean consensus — it means informed decision-making. Useful input sources:
- Engineers who will implement and maintain the decision
- Engineers who have made similar decisions at previous companies
- The team that owns adjacent systems affected by the decision
- Written resources: vendor documentation, blog posts from companies at similar scale
Step 3: Evaluate Options
Compare options against explicit criteria. Weight the criteria by importance. Be honest about unknowns.
Step 4: Decide & Document
Make the decision. Write the ADR. Communicate it to affected parties. Do not let the decision linger — a good decision made promptly is better than a perfect decision made too late.
Step 5: Set a Review Date
For significant decisions, set a date to revisit. "We will evaluate this choice in 6 months based on performance data." This removes the pressure of making the perfect decision now and creates a natural point to course-correct.
Common Decision Traps
Resume-Driven Development
Choosing technology because it looks good on your resume, not because it solves the problem. The boring technology that your team knows well is almost always the right choice.
Real-world example: a team adopted Kubernetes for a simple three-service application because the tech lead wanted Kubernetes experience. The operational overhead consumed 30% of the team's capacity for six months. They eventually moved back to a simpler deployment model. The boring choice (a few EC2 instances behind a load balancer) would have taken a day to set up instead of months to stabilize.
Analysis Paralysis
Gathering more and more data to avoid the discomfort of deciding. Set a time box. If you cannot decide in a week, the decision is either reversible (just pick one) or you need to reduce scope.
A useful intervention: ask "What additional information would change my decision?" If the answer is "nothing realistic," you already have enough information. Decide.
The Loudest Voice Wins
In group discussions, confidence is not correlated with correctness. Structure your decision-making: gather input async, evaluate against criteria, decide. Do not let the meeting dynamics determine architecture.
A practical technique: before a decision meeting, ask participants to write their recommendation and reasoning independently. Review the written input before discussing. This prevents anchoring bias and ensures quieter team members' perspectives are heard.
Sunk Cost
"We have already spent three months on this approach" is not a reason to continue. The only question is: given what we know now, what is the best path forward? The time already spent is gone regardless of what you decide next.
Premature Optimization
Making decisions based on scale you do not have. "We need to use Cassandra because we might have billions of rows someday" when you currently have thousands. Design for your current scale with a migration path to the next order of magnitude. Do not design for three orders of magnitude ahead.
Herd Mentality
Choosing a technology because Netflix or Google uses it. These companies have different problems, different scales, and different resources than you do. Their technical decisions are optimized for their context, not yours.
Building a Decision-Making Culture
As a senior engineer, you do not just make decisions — you shape how your team makes decisions. The patterns you establish become the team's default approach.
Teaching Others to Decide
When a mid-level engineer comes to you with a decision, resist the urge to just give them the answer. Instead, walk them through the framework:
- "Is this reversible or irreversible?"
- "What are the trade-offs? Can you write them down?"
- "Who else is affected by this decision?"
- "What would you recommend, and why?"
Over time, they internalize the framework and stop needing your input on routine decisions. This is force multiplication applied to decision-making.
Creating Decision-Making Defaults
Teams waste enormous amounts of time on decisions that should be defaults. Establishing team defaults — "We use PostgreSQL unless there is a specific reason not to," "We default to REST APIs for external services," "We write integration tests for all API endpoints" — eliminates hundreds of small decisions per quarter.
Good defaults are opinionated but overridable. They reduce cognitive load for common cases while allowing exceptions when the situation demands it.
Making Decisions Under Uncertainty
You will never have complete information. Senior engineers are comfortable making decisions with 70% confidence and adjusting as they learn more. Waiting for 100% confidence means someone else made the decision for you — usually the deadline.
The key is to design for learning: ship small, measure, iterate. Structure your decisions so that new information can change your course without starting over.
Techniques for Deciding Under Uncertainty
- Prototype first. For technology choices, build a small prototype that tests the critical assumption. Two days of prototyping tells you more than two weeks of research.
- Use feature flags. Deploy behind a flag so you can roll back without a code deployment.
- Set tripwires. Define in advance what signals would tell you the decision was wrong. "If P99 latency exceeds 500ms, we will revisit the architecture."
- Make the decision two-way. Before committing to an approach, ask: "What would we need to do to reverse this?" If the reversal cost is low, you can decide with less confidence.
Common Pitfalls
- Deciding alone. Even if you are the most experienced engineer on the team, gathering input makes decisions better and builds buy-in. Decisions made in isolation are harder to execute because the team was not part of the process.
- Not deciding. Indecision is a decision — it is a decision to let circumstances choose for you. If you have a deadline and no decision, you will end up with whatever is fastest to implement, which is rarely the best option.
- Over-documenting trivial decisions. Not every choice needs an ADR. Save the formal process for decisions that are irreversible, expensive, or affect multiple teams.
- Ignoring operational cost. Engineers love to evaluate technology choices on developer experience and features. They often underweight operational burden: monitoring, debugging, upgrades, and on-call complexity. A tool that is delightful to develop with but miserable to operate in production is a bad choice.
- Confusing consensus with quality. A decision everyone agrees with is not necessarily a good decision. Sometimes the right call is unpopular. Your job is to make the best decision, not the most popular one.
Key Takeaways
- Classify decisions as reversible or irreversible — apply rigor proportional to the permanence of the choice
- Make trade-offs explicit by writing them down — most bad decisions come from invisible costs
- Use ADRs to create institutional memory and prevent the same debates from recurring every quarter
- Frame the problem clearly before evaluating solutions — solving the wrong problem perfectly is still failure
- Set time boxes to prevent analysis paralysis — if more data would not change your decision, decide now
- Design for learning: prototype, use feature flags, set tripwires, and revisit decisions on a schedule
- Beware resume-driven development, sunk cost fallacy, and herd mentality — the boring technology your team knows is usually the right choice
- Gathering input is not the same as seeking consensus — make the best decision, not the most popular one