Fritz Hut | Thoughts & Commentary

Art, culture, and the conversations that matter.

Archives (page 2 of 12)

The Difference Between a Startup That Ships and a Startup That Talks About Shipping

I’ve spent two decades inside software teams that couldn’t ship a goddamn thing, and a handful that moved so fast it felt like a continuous deployment pipeline for the soul. The difference was never the tech stack. It was never the funding. It was never the presence or absence of a foosball table. The difference was a set of invisible, self-reinforcing organizational patterns—what I’ve come to call the sociotechnical event loop. When that loop is blocked, you get a startup that talks about shipping. When it’s unblocked, you get a startup that actually ships.

This isn’t a motivational post. I’m not going to tell you to “move fast and break things.” That’s a bumper sticker for people who’ve never had to fix a production database at 2 a.m. Instead, I’m going to dissect the hidden architecture of team behavior—the race conditions, blocking calls, and deadlocks that live not in your codebase, but in your standups, your planning meetings, and your Slack channels.

Two engineers debugging a whiteboard covered in system diagrams, one pointing at a bottleneck
Every shipping problem is a people problem that’s been politely ignored for six months.

The Sociotechnical Event Loop: Why Your Team Is Stuck in a Blocking Call

In software, a blocking call is a function that halts execution until some external resource responds. Your team has its own version. It’s the meeting where no one will admit they don’t understand the requirements. It’s the pull request that sits open for three weeks because the reviewer is “too busy.” It’s the architecture decision that gets deferred to a committee that never meets. Each of these is a blocking call on your team’s ability to deliver value. And just like in code, a single blocking call in a critical path can freeze the entire system.

Startups that ship have learned to identify their organizational blocking calls and either make them asynchronous or eliminate them entirely. They treat decision latency as a bug, not a feature of “being thorough.” They understand that a bad decision today, corrected tomorrow, is cheaper than a perfect decision made three months late. The market is a ruthless integration test, and it doesn’t wait for your architecture review board to convene.

The Decision Queue and Its Discontents

Every team has a decision queue. In a healthy system, this queue is processed with predictable throughput. Decisions flow from “proposed” to “decided” in hours or days, not weeks. In a talking-about-shipping startup, the decision queue is a black hole. Items enter, but nothing emerges. The team compensates by building elaborate scaffolding around the void—more meetings, more documents, more alignment sessions—none of which actually resolve the underlying block.

I once consulted for a company that had spent four months debating whether to use a monorepo or polyrepo. Four months. The engineers were writing design docs, running benchmarks, and having philosophical debates about dependency management. Meanwhile, the product they were supposed to be building hadn’t shipped a single feature. The decision queue was blocked, and the entire team was in a spinlock, burning CPU cycles on nothing.

The fix wasn’t technical. It was cultural. We flipped a coin, picked a repo structure, and agreed to revisit the decision after shipping three features. The team shipped more in the next two weeks than they had in the previous four months. The sociotechnical event loop was unblocked.

Legacy Systems Are People, Too

In code, a legacy system is something that’s hard to change because nobody fully understands it, and the original authors are long gone. In teams, legacy systems are the unwritten rules, the tribal knowledge, and the “that’s how we’ve always done it” reflexes that accumulate over time. A startup that talks about shipping is usually running on a legacy operating system of fear: fear of breaking things, fear of looking stupid, fear of being blamed.

I call this the Blame-Driven Development (BDD) pattern. In BDD, every decision is optimized not for customer value, but for personal defensibility. Engineers write overly complex solutions because if it fails, they can point to the design doc and say, “See? We thought of everything.” Product managers add endless acceptance criteria to avoid being held responsible for a missed edge case. The result is a system so over-engineered that it never leaves staging.

A tangled mess of cables and wires representing technical debt in team communication
Blame-Driven Development produces the same kind of mess as a decade-old codebase with no refactoring discipline.

Startups that ship have replaced BDD with what I call Learning-Driven Development (LDD). In LDD, the primary goal of shipping is to get a signal from reality. A failed experiment isn’t a career-limiting move; it’s a data point. The team’s psychological safety is high enough that people can say, “I don’t know, let’s find out,” without fearing a retroactive performance review. This isn’t soft-skills fluff. It’s a hard requirement for any system that needs to adapt quickly. A team without psychological safety is like a distributed system where every node is running a different consensus algorithm. It will never converge on a decision.

Refactoring the Blame Culture

Refactoring a team’s culture is harder than refactoring code, because you can’t just write a unit test to confirm the behavior changed. But the principles are the same: make small, observable changes; get feedback; iterate. Start with the retrospective. If your retros are a polite recitation of what went well and what could be improved, with no concrete action items, you’re not doing a retro. You’re doing a group therapy session that nobody asked for.

A shipping team’s retro is a debugging session. You identify the root cause of the most painful failure, you assign an owner, and you fix it before the next sprint. Not a process document. Not a “let’s be more careful next time.” A real, observable change to how the team works. Maybe it’s a new linting rule. Maybe it’s a change to the on-call rotation. Maybe it’s telling the CTO to stop joining standup and asking “status” questions that make everyone defensive. Whatever it is, it’s a code change to the team’s operating system.

Race Conditions in the Standup

A race condition in software happens when two processes access shared data and the outcome depends on the order of execution. In teams, race conditions happen when multiple stakeholders compete for the same resource—usually engineering attention—without a clear priority mechanism. The result is thrashing: context switching so severe that nothing gets finished.

I’ve seen startups where the CEO, the VP of Sales, and a key customer all have direct access to individual engineers. They ping them on Slack, tap them on the shoulder, and inject “urgent” requests into the sprint. The engineers, trying to be helpful, context-switch constantly. The sprint board becomes a work of fiction. The team is busy, but nothing ships. This is a classic race condition: multiple uncoordinated writers to a shared resource, with no mutex or semaphore in sight.

The fix is a priority queue with a single gatekeeper. In practice, this means a product manager or engineering lead who says “no” or “not now” with the authority of the entire team behind them. It’s not about being a jerk. It’s about protecting the team’s ability to execute. Without a gatekeeper, the team’s attention is a public good that gets tragically overconsumed.

The Cost of Context Switching

Context switching isn’t just annoying. It’s computationally expensive. Research on task switching shows that it can take over 23 minutes to fully regain focus after an interruption. If your engineers are getting interrupted three times a day, they’re losing over an hour of productive time. That’s a 12.5% tax on your engineering capacity, and it compounds. The startup that ships protects its engineers’ focus like a scarce resource, because it is one.

The Synchronous Standup Antipattern

Daily standups are supposed to be a quick sync. In practice, they often become a blocking call that wastes 30 minutes of every engineer’s morning. The standup is a synchronous operation in an otherwise asynchronous system. It forces everyone to be available at the same time, which is especially painful for remote teams across time zones.

Shipping teams replace synchronous standups with asynchronous updates in a shared channel. They use the freed-up time for actual collaboration: pair programming, design reviews, or just uninterrupted deep work. The standup becomes a written log, not a performance. If you need a meeting to find out what your team is doing, you have a visibility problem, not a scheduling problem.

A team huddled around a monitor, one person pointing at a deployment pipeline dashboard
Shipping teams replace status meetings with visible, automated signals from their delivery pipeline.

The Deployment Pipeline as a Sociotechnical Mirror

Your CI/CD pipeline isn’t just a tool. It’s a mirror reflecting your team’s actual behavior. A pipeline with flaky tests, manual approval gates, and deployment windows that nobody respects is a symptom of a team that doesn’t trust its own work. A clean, fast, reliable pipeline is a sign of a team that has invested in quality and automation—not just in code, but in process.

I’ve seen teams spend months “setting up CI/CD” without ever shipping to production. They’re optimizing a system that doesn’t deliver value. It’s like tuning a race car engine while the car is still on cinder blocks. The shipping team’s approach is different: they get a minimal pipeline working, deploy something—anything—to production, and then improve the pipeline based on real feedback. The pipeline is a product, and the team is its first user.

Flaky Tests Are a Cultural Problem

Flaky tests are often treated as a technical nuisance. They’re not. They’re a signal that the team tolerates unreliability. When a test fails intermittently, a shipping team treats it as a P0 bug and either fixes it or deletes the test. A talking-about-shipping team adds it to the “flaky test quarantine” and learns to ignore the red build. Over time, the build is always red, and nobody trusts it. The pipeline becomes a dead letter box. The team has trained itself to ignore failure signals, which is exactly the opposite of what a healthy sociotechnical system should do.

Architecture as a Social Construct

Conway’s Law states that organizations design systems that mirror their communication structure. I’d add a corollary: teams that can’t communicate directly will produce systems that can’t communicate directly. If your backend team and frontend team are in separate silos, you’ll end up with a brittle API contract that nobody wants to change, because changing it requires a cross-team meeting that takes three weeks to schedule. The architecture becomes a fossilized record of past organizational dysfunction.

Shipping startups invert this. They design the team structure to match the desired architecture. If they want a modular, loosely coupled system, they create small, autonomous teams with clear ownership boundaries. They use internal APIs not just in code, but in how teams communicate: well-defined interfaces, explicit contracts, and a shared understanding that you don’t break the contract without talking to the other team first. This is the sociotechnical equivalent of microservices done right.

The Two-Pizza Rule, Revisited

Amazon’s two-pizza rule—teams should be small enough to feed with two pizzas—isn’t about catering. It’s about minimizing communication overhead. In a team of n people, the number of communication channels grows as n(n-1)/2. A team of 6 has 15 channels. A team of 12 has 66. The talking-about-shipping startup ignores this math and creates “cross-functional squads” of 15 people, then wonders why nothing gets decided. The shipping startup keeps teams small and gives them clear, independent missions. The communication overhead stays manageable, and decisions happen at the edge, not in a centralized bottleneck.

FAQ: Diagnosing Your Team’s Shipping Blockers

How do I know if my team is in a blocking call?

Look for decisions that have been “in discussion” for more than a week with no resolution. Look for pull requests that have been open for more than two days without a review. Look for meetings where the same topic is discussed repeatedly without a clear owner or deadline. Each of these is a blocking call. The fix is to assign a single decision-maker, set a hard deadline, and make the decision with the information available. If it’s wrong, you’ll learn and correct. Indecision is a decision to do nothing, and it’s the most expensive option.

What’s the difference between a good process and bureaucratic overhead?

A good process reduces the cognitive load on the team and increases predictability. Bureaucratic overhead increases cognitive load and creates busywork. The test is simple: if you removed the process tomorrow, would the team ship faster or slower? If faster, it’s overhead. If slower—because quality would drop or coordination would break—it’s a good process. Be honest. Most “process” in startups is just anxiety made visible.

How do we stop the CEO from injecting urgent requests directly to engineers?

This is a boundary problem. The CEO has legitimate needs, but the current communication pattern is creating a race condition. The solution is to give the CEO a better interface: a single point of contact (usually the engineering lead or product manager) who triages requests and provides visibility into when they’ll be addressed. The key is to make this interface faster and more reliable than going directly to engineers. If the CEO gets a clear answer and a predictable timeline, they’ll stop bypassing the system. If they don’t, they’ll keep doing end-runs. The interface has to be better than the workaround.

What if our team is too small to have dedicated roles like product manager or engineering lead?

Then someone wears multiple hats, but the functions still need to exist. The priority queue gatekeeper might be the same person who writes code, but they need explicit authority to say “no” and the team’s agreement to respect that. In very small teams, this can be a rotating role. The important thing is that the role exists and is acknowledged. Without it, you’re back to the race condition.

Conclusion: The Sociotechnical Refactor

The difference between a startup that ships and one that talks about shipping isn’t talent, funding, or luck. It’s the health of the sociotechnical event loop. It’s the ability to identify and remove blocking calls, refactor legacy cultural patterns, prevent race conditions, and design team structures that enable flow rather than impede it.

This is the real engineering work. The code is the easy part. The hard part is debugging the human system that produces the code. Start treating your team’s culture with the same rigor you apply to your architecture, and you’ll be amazed at how fast you can ship. Or don’t, and keep talking about it. The market will eventually make the decision for you, and it’s not known for its patience.

The Difference Between a Startup That Ships and One That Just Talks About Shipping

You know the scene. Daily standup rolls around, and every single person chants “no blockers” like a prayer. Then three sprints slide by and nothing actually moves. The roadmap is a gorgeous Gantt chart that nobody in the room believes. The CTO runs a town hall about “velocity” while the codebase rots into a haunted house of half-baked feature flags and TODO comments from 2019. That’s the startup that talks about shipping. The one that actually ships? It’s quieter. Messier in the right ways. It treats engineering culture like a production system, not a motivational poster. The gap between these two companies isn’t talent, funding, or tooling. It’s a set of organizational patterns that either create throughput or simulate it. And if you’ve been around long enough, you can smell the difference by Wednesday of your first week.

This isn’t another “agile vs. waterfall” sermon or a rant about hustle culture. It’s about the structural defaults that turn an engineering team into a delivery engine or a theater troupe. The talking startup owns all the artifacts of progress—standups, retros, sprint reviews, OKRs—but none of the actual throughput. The shipping startup runs on a different set of patterns: tight feedback loops, ruthless prioritization, and a deep, almost physical allergy to fake work. Let’s walk through the real differences, seen through the eyes of someone who’s debugged both kinds of organizations.

The Core Pattern: Feedback Loops vs. Status Loops

In systems thinking, a feedback loop uses its own output to adjust future behavior. A thermostat is a feedback loop: it measures temperature, compares it to a set point, and flips the heat on or off. A shipping startup runs on feedback loops. A talking startup runs on status loops—a degenerate form where the output is just another meeting invite.

Here’s the tell. In a shipping startup, a feature goes from idea to production in days or weeks, and the team immediately stares at usage data, error rates, and customer reactions. That data changes the next thing they build. The loop is closed. In a talking startup, the same feature wades through a six-week “discovery” phase, three rounds of stakeholder alignment, a Jira epic with 47 subtasks, and then sits in a staging environment for two months because “we need to coordinate the launch with marketing.” The output of the process isn’t a shipped feature—it’s another status update. The team is running a blocking call on itself.

This is the organizational equivalent of a race condition. Multiple processes are waiting on each other, and the system never reaches a consistent state. The talking startup has built a distributed system where every component is waiting for a mutex that another component holds. The shipping startup has designed its processes to be non-blocking. It defaults to asynchronous communication, small batch sizes, and continuous deployment. The talking startup defaults to synchronous meetings, large batch releases, and a release process that requires sign-off from someone who’s on vacation.

The Artifact Problem: When Outputs Become Inputs

In a healthy engineering culture, the primary artifact is working software. Everything else—specs, tickets, design docs, status reports—is scaffolding. It exists to support the creation of working software, and it should be as lightweight as possible. In a talking startup, the scaffolding becomes the product. The artifacts themselves are treated as deliverables, and people are evaluated on the quality of their artifacts rather than the quality of their software.

I once consulted for a company where the engineering manager proudly showed me their “decision log.” It was a 40-page Confluence document with every architectural decision from the past two years, complete with alternatives considered, tradeoff matrices, and stakeholder sign-offs. It was a beautiful document. The product had shipped exactly one feature in that same period. The decision log wasn’t a tool for making decisions—it was a tool for avoiding them. Every time someone proposed actually building something, the response was “let’s document the decision first.” The documentation had become a blocking call on the entire engineering org.

This is a classic legacy system pattern. In old codebases, you often find layers of abstraction that were added to solve a problem that no longer exists, but now they’re load-bearing and nobody dares remove them. The talking startup has the same thing, but with process. The “decision log” was originally created to bring transparency to a chaotic early stage. But the chaos is gone, and now the process is just adding latency. The shipping startup treats process like code: if it’s not serving a clear purpose, you refactor it or delete it. The talking startup treats process like a sacred text.

Incentive Architecture: What Actually Gets Rewarded

Every organization has an incentive architecture, whether it’s designed intentionally or not. In a shipping startup, the incentive architecture is aligned with throughput. Engineers are rewarded for getting things into production, measuring the impact, and iterating. In a talking startup, the incentive architecture rewards activity that looks like work but isn’t. Writing detailed tickets, giving impressive demos, participating in cross-functional alignment meetings—these are all visible, high-status activities that can be rewarded even if nothing ships.

This creates a perverse feedback loop. The people who are best at looking busy get promoted. They then hire more people who are good at looking busy. The people who actually want to ship things either leave or get marginalized. The system becomes a self-reinforcing bureaucracy, like a memory leak that slowly consumes all available resources until the application crashes. I’ve seen this happen in real time: a startup raises a Series A, hires a bunch of “experienced” managers from big tech, and within six months the shipping velocity drops to zero. The managers aren’t bad people. They’re just running the only operating system they know, and that operating system was designed for a different hardware architecture.

The fix isn’t to fire all the managers. It’s to change the incentive architecture. Reward shipped features, not planned features. Reward resolved incidents, not incident reports. Reward customer problems solved, not meetings attended. This sounds obvious, but it’s shockingly rare. Most startups have a performance review system that measures individual activity rather than system throughput. It’s like optimizing for CPU utilization instead of request latency—you can have 100% CPU utilization and still serve zero requests if all the cycles are going to garbage collection.

The Fear Tax

Underneath all the process and bureaucracy of a talking startup is usually fear. Fear of breaking something. Fear of looking bad. Fear of making the wrong decision. The talking startup deals with fear by adding more process—more reviews, more sign-offs, more meetings. Each layer of process reduces the probability of a visible failure, but it also reduces the probability of any visible success. The shipping startup deals with fear by making failures cheap. If you can deploy a fix in five minutes, a bug in production is annoying but not catastrophic. If your release process takes two weeks, every bug is a potential career-ending event, so you add more process to prevent bugs, which makes releases even slower, which makes bugs even scarier. It’s a death spiral.

I call this the fear tax. It’s the overhead you pay to avoid being blamed for something. In a shipping startup, the fear tax is low because the system is designed to absorb failures gracefully. Feature flags, canary deployments, automated rollbacks, observability—these are the technical tools that reduce the blast radius of a mistake. But the real tool is cultural: a shared understanding that failures are learning opportunities, not firing opportunities. In a talking startup, the fear tax is high because the system is brittle and blame is personal. Every decision is a potential career risk, so nobody makes decisions. The system freezes.

Why “Move Fast and Break Things” Was Half Right

The old Facebook motto gets a lot of mockery now, but it captured something real. The “move fast” part is essential for any startup that wants to survive. The “break things” part is what people misunderstand. It doesn’t mean be reckless. It means design your systems so that breaking things is safe. A shipping startup invests heavily in making breakage cheap: feature flags, staged rollouts, automated testing, fast rollbacks. A talking startup invests in making breakage impossible, which is itself impossible, so they end up just moving slowly and still breaking things, but now the breakages are catastrophic because they’ve accumulated so much change between releases.

This is the difference between a circuit breaker and a fuse. A circuit breaker trips quickly, prevents damage, and can be reset. A fuse blows and needs to be replaced. Talking startups are full of fuses. Shipping startups use circuit breakers. The technical term is “blast radius reduction,” and it’s one of the most important concepts in modern software engineering. But it applies to organizations too. A shipping startup reduces the blast radius of decisions by making them small, reversible, and fast. A talking startup increases the blast radius by batching decisions into large, irreversible commitments that require months of alignment.

The Meeting-to-Deploy Ratio

If I had to pick one metric to diagnose an engineering org, it would be the meeting-to-deploy ratio. How many hours of meetings does it take to get one pull request into production? In a shipping startup, the ratio is low—sometimes zero. Engineers write code, get it reviewed, merge it, and it’s in production within hours. In a talking startup, the ratio is astronomical. A single deploy might require a design review meeting, a security review meeting, a go-to-market meeting, a release planning meeting, and a post-deploy validation meeting. The deploy itself might take 30 minutes. The meetings take 30 hours.

This is the organizational equivalent of N+1 queries. In database performance, an N+1 query is when you fetch a list of items and then make a separate query for each item’s related data, resulting in exponential overhead. A talking startup does this with communication. Instead of having a single async channel where decisions are made and documented, they have a separate meeting for each stakeholder group, each one rehashing the same context, each one generating new action items that spawn more meetings. The overhead grows quadratically with the number of people involved.

The shipping startup optimizes for async communication. Decisions are made in writing, in the open, with clear owners and deadlines. Meetings are for resolving ambiguity, not for sharing status. Status is a byproduct of the work itself—visible in the commit log, the deploy pipeline, the feature flag dashboard. If you need a meeting to know what’s happening, your system is already broken.

The “Senior Engineer as Organizational Therapist” Problem

In a talking startup, senior engineers spend most of their time not writing code but managing the organizational dysfunction. They sit in alignment meetings. They translate between teams that should be talking directly to each other. They write documents to justify decisions that should be obvious. They are therapists for a system that has become its own patient.

This is a tragedy because senior engineers are the most expensive and most capable people in the org, and they’re being used as glue work to hold together a broken process. In a shipping startup, senior engineers are force multipliers. They write the critical path code, they design the systems that let junior engineers move faster, they remove bottlenecks. They’re not in meetings all day because the system doesn’t require constant human intervention to function. The system itself is the senior engineer’s force multiplier.

If you’re a senior engineer in a talking startup, you have a choice. You can keep being the therapist, or you can start treating the organizational system as your real project. Refactor the process. Delete the meetings that don’t have clear outcomes. Automate the status reporting. Make the work visible without requiring a meeting. This is systems thinking applied to the org chart, and it’s the most impactful code you’ll never write.

Practical Patterns for Shipping

So what do you actually do if you’re stuck in a talking startup and want to turn it into a shipping startup? Here are some patterns I’ve seen work, field-tested in real orgs with real scar tissue.

1. Make Work Visible Without a Meeting

If your team’s status is communicated primarily through standups and weekly reports, you have a visibility problem. The fix is to make the work itself visible. Use a physical or digital board that shows the actual state of every work item: not just “in progress,” but where it is in the pipeline. Is it waiting for review? Is it in staging? Is it behind a feature flag? The board should be a real-time representation of the system’s state, not a manual update that someone fills in before the standup. If you need a standup to know what’s happening, your board is lying to you.

2. Kill the Blocking Call

Identify every process that requires a synchronous handoff between teams and ask: can this be async? Design reviews don’t need a meeting. They need a document, a deadline for comments, and a clear decision-maker. Status updates don’t need a meeting. They need a dashboard. Cross-team coordination doesn’t need a meeting. It needs a shared channel with clear ownership and SLAs. Every meeting you eliminate is a mutex you’ve removed from the system.

3. Make Small Bets, Not Big Plans

Talking startups love big plans because big plans are impressive and hard to falsify. If you plan a six-month project, nobody can prove you’re wrong until six months later, and by then everyone’s forgotten the original plan anyway. Shipping startups make small bets: two-week experiments, one-day spikes, features behind feature flags that can be killed if they don’t work. The goal is to maximize the number of learning cycles per unit time. A small bet that fails teaches you something. A big plan that never ships teaches you nothing.

4. Measure Throughput, Not Activity

Stop measuring story points, velocity, or “dev days.” These are activity metrics, not throughput metrics. They measure how busy people look, not how much value they’re delivering. Measure cycle time: how long does it take for an idea to go from “we should do this” to “it’s in production and customers are using it”? Measure deploy frequency. Measure mean time to recovery. These are the metrics that actually correlate with organizational performance, as the DORA research program has shown. If your metrics don’t tell you whether you’re shipping, you’re measuring the wrong things.

5. Make Failure Cheap

Invest in the technical practices that reduce the cost of failure: automated testing, continuous integration, feature flags, canary deployments, observability. But also invest in the cultural practices: blameless postmortems, psychological safety, a shared understanding that mistakes are system failures, not personal failures. When failure is cheap, people take more risks, which means they ship more things, which means they learn faster. When failure is expensive, people play defense, which means they ship nothing.

The Real Cost of Talking

The talking startup isn’t just slower. It’s more expensive in ways that don’t show up on a balance sheet. The best engineers leave because they want to build things, not attend meetings. The remaining engineers become demoralized and stop caring. The product falls behind competitors who are shipping faster. The company burns through its runway without generating real learning. Eventually, the talking startup becomes a case study in how not to run an engineering org.

The shipping startup isn’t perfect. It has its own problems: technical debt, occasional outages, the constant tension between speed and quality. But these are problems of a living system. The talking startup’s problems are problems of a dead system. One is a race condition you can debug. The other is a system that’s hung forever, waiting for a mutex that will never be released.

Frequently Asked Questions

How can I tell if my startup is a talking startup or a shipping startup?

Look at the last three features your team “completed.” Are they in production, being used by real customers, with measurable impact? Or are they “done” according to Jira but not actually live? If it’s the latter, you’re in a talking startup. Another test: count how many meetings happened this week that were about the work rather than doing the work. If the ratio is above 1:1, you have a problem.

What’s the first thing I should change if I want to move from talking to shipping?

Make work visible without a meeting. Set up a dashboard or a board that shows the real-time state of every work item in the pipeline. Then, in your next standup, point to the board instead of giving verbal updates. If the board is accurate, the standup becomes redundant. If the board isn’t accurate, you’ve just identified your first problem to fix. This single change often reveals a cascade of other dysfunctions that were being hidden by the status-reporting theater.

Isn’t some process necessary to prevent chaos?

Yes, but process should be pull-based, not push-based. A pull-based process is one that people use when they need it—like a library they import. A push-based process is one that’s imposed on everyone regardless of need—like a mandatory middleware that runs on every request. The talking startup has push-based process: everyone must attend standup, everyone must write a weekly status report, everyone must get sign-off from the architecture review board. The shipping startup has pull-based process: if you need architectural guidance, you can request a review; if you need to coordinate with another team, you can post in a shared channel. The process exists, but it’s opt-in, not mandatory.

How do I convince leadership to change?

Don’t try to convince them with arguments. Show them with data. Start measuring cycle time, deploy frequency, and mean time to recovery. Make these metrics visible to the entire org. When leadership sees that it takes six weeks to ship a button color change, they’ll start asking their own questions. The key is to make the cost of talking visible. Right now, the talking startup’s dysfunctions are hidden behind activity metrics that make everyone look busy. Surface the throughput metrics, and the conversation changes from “we need more process” to “why is our process so slow?”

What if the problem is the CTO or VP of Engineering?

This is the hardest case. If the person at the top is the one creating the talking culture, you have limited options. You can try to influence them with data and concrete proposals for small experiments. You can build a shipping subculture within your own team and hope it spreads. But if the leadership is fundamentally committed to the talking model, you may need to make a career decision. Life is too short to spend it in meetings about work you’re not allowed to ship. The market for engineers who can actually deliver is strong, and shipping startups are always hiring.

The difference between a startup that ships and one that talks about shipping isn’t a mystery. It’s a set of observable, diagnosable, fixable patterns. The question is whether you’re willing to treat your org chart as a system worth debugging. Most people aren’t. They’d rather complain about meetings than eliminate them. They’d rather add process than remove it. They’d rather be busy than be effective. Don’t be most people. Ship something.

A team collaborating around a table with laptops and notebooks, focused on a shared goal
Close-up of hands typing on a laptop keyboard, representing the act of building and shipping code
A whiteboard filled with diagrams and sticky notes, illustrating system design and process mapping

The Shipping Gap: Why Some Teams Deliver and Others Just Talk About It

There’s a certain kind of meeting that makes me want to reformat my own brain. You know the one. A startup that’s raised eight figures, hired thirty people, and spent eighteen months “building” has exactly zero paying customers. But the slide deck? Immaculate. The roadmap? A thing of beauty, color-coded by quarter, full of phrases like “delight the user” and “unlock synergies.” The team is always two weeks away from a breakthrough. They’ve been two weeks away since the Obama administration. This isn’t a funding problem or a talent problem. It’s a shipping culture problem—a race condition in the organizational logic where the act of preparing to ship has completely replaced the act of shipping.

I’ve spent my career inside engineering teams, from bootstrapped startups to Fortune 500 behemoths, and I’ve learned to spot the difference between a team that ships and a team that talks about shipping within about fifteen minutes of walking in the door. It’s not the tech stack. It’s not the pedigree of the engineers. It’s the organizational sociology—the hidden patterns of communication, fear, and incentive that act as the real operating system of the team. The codebase is just a lagging indicator. The culture is the source code. And most “shipping” cultures have a bug in the main loop: they’ve confused motion with progress, and they’re stuck in a blocking call they don’t even see.

A team huddled around a laptop, one person pointing at the screen while others look on with focused expressions.
Shipping teams huddle to unblock, not to plan the next planning session.

The Real Difference: A Systems View

Let’s define the entity we’re actually debugging. A shipping culture is an organizational system where the default outcome of work is a tangible, user-facing change in production, on a regular cadence, with minimal ceremony. A talking culture is one where the default outcome is an artifact about the work: a spec, a roadmap update, a slide, a Jira ticket moved to “In Review,” a Slack thread that ends with “let’s circle back.” Both systems have engineers writing code. But in the talking culture, the code is a side effect of the process. In the shipping culture, the process is a side effect of the code.

This isn’t a binary. It’s a spectrum, and every team slides along it based on the incentives they’ve built into their own feedback loops. The key adjacent concepts here are deployment frequency, lead time for changes, blameless post-mortems, and cognitive load. You can’t fix a talking culture by yelling “ship faster.” You have to rewire the feedback loops so that talking becomes more expensive than shipping.

The Blocking Call: Fear of the Unpolished Artifact

In software, a blocking call is a function that halts execution until something else completes. In a talking culture, the blocking call is internal approval. The team can’t ship until the VP of Product “aligns” with the Head of Design, who needs to run it past the CEO, who wants to see a “more complete” prototype. Meanwhile, the prototype is already complete enough to solve a real problem for a real user, but nobody will know that because it’s trapped in a Figma file and a Notion doc.

I once consulted for a startup that had spent four months designing a “perfect” onboarding flow. They had user personas, journey maps, a 60-page PRD. I asked how many users they’d actually watched try to sign up. Zero. They were optimizing a system they’d never observed in production. The fix wasn’t to do more research. It was to ship a janky, embarrassing version of the flow to five people and then iterate. The team was terrified of looking stupid. But shipping cultures understand that looking stupid in front of five users is cheap; looking stupid in front of your board after burning $2 million on an unlaunched product is a career-ending move.

This fear of the unpolished artifact is a legacy compatibility issue. Many engineers and product managers were trained in big-company environments where a “launch” was a coordinated, high-stakes event with marketing campaigns and press briefings. They bring that mental model to a startup, where a launch should be a quiet, boring, daily occurrence. The talking culture treats every release like a moon landing. The shipping culture treats it like a bus schedule.

Incentive Architecture: What You Reward Is What You Get

If you want to diagnose a team’s culture, ignore the mission statement and look at what gets celebrated. In a talking culture, people are rewarded for planning to ship. They get kudos in standup for “unblocking a dependency” or “aligning stakeholders.” The roadmap presentation gets applause. The engineer who spent three weeks writing a design doc is praised for their thoroughness. None of these things are bad in isolation. But when they become the primary currency of career advancement, shipping becomes a distraction from the real work of looking busy.

In a shipping culture, the rewards are inverted. The engineer who hacks together a feature, deploys it on a Friday afternoon (yes, Friday), and has real users banging on it by Monday morning is the hero—even if the code is a bit ugly. The product manager who kills a feature that nobody uses is celebrated for reducing complexity. The team that runs a two-day experiment and writes a one-paragraph summary of the results is seen as more effective than the team that spent a month on a “comprehensive” analysis. The incentive structure is a race condition: if you reward activity over outcomes, the system will optimize for looking busy, and shipping will always lose the race.

A person typing on a laptop with a coffee cup nearby, focused on the screen.
Shipping cultures reward the work that actually reaches users, not the work that looks good in a status update.

The Legacy System of Trust

Here’s a pattern I’ve seen kill more startups than any technical debt: the trust legacy system. In a talking culture, trust is a scarce resource that must be earned through a series of bureaucratic rituals. Before you can deploy to production, you need sign-off from QA, security, the product owner, and maybe someone’s cousin who “knows the system.” This creates a massive queue of blocked work and a culture where nobody feels responsible for the outcome because everyone had to approve the process.

Shipping cultures invert this. They start with high trust and remove it only when you prove you can’t be trusted. A new engineer gets access to production on day one. They’re expected to deploy something small in their first week. If they break something, the system is designed to catch it quickly and roll it back without drama—a blameless post-mortem, not a blameful inquisition. This is a systems-thinking approach: the team treats failures as bugs in the process, not character flaws in individuals. As a result, people ship more, learn faster, and break things less often over time because the feedback loop is so tight.

I’ve seen teams where a single bad deploy six months ago led to a “deployment review board” that now meets twice a week and requires three sign-offs for any change. That board is a deadlock. It’s a mutex that only one person can hold, and everyone else is waiting. The talking culture builds process to prevent the last disaster. The shipping culture builds resilience to survive the next one.

The Architecture of Shipping: Small Batches, Tight Loops

If you want to shift a team from talking to shipping, you don’t start with a motivational speech. You start with the batch size. Large batches are the root cause of talking cultures. When a feature takes three months to build, the stakes feel enormous. Everyone wants to review it, polish it, de-risk it with meetings. The talking is a rational response to the batch size. Shrink the batch, and you shrink the need for ceremony.

I’ve seen teams go from quarterly releases to daily deploys not by adopting microservices or Kubernetes, but by simply asking: “What’s the smallest thing we can ship this week that would teach us something?” That question reframes the work from “deliver a feature” to “run an experiment.” Experiments are allowed to fail. Features are not. The talking culture is built on the fear of shipping the wrong feature. The shipping culture is built on the certainty that you’ll learn something either way.

This is where continuous deployment and feature flags become cultural tools, not just technical ones. A feature flag lets you decouple deployment from release. You can ship code to production every hour without exposing it to users until it’s ready. This removes the bottleneck of “is it done?” and replaces it with “is it safe to turn on?” The talking culture argues about done-ness in meetings. The shipping culture flips a toggle and watches the metrics.

The Talking Culture’s Favorite Artifacts

Let’s get specific. Here are the artifacts that correlate almost perfectly with a talking culture:

  • The roadmap that looks like a Gantt chart from 2003. If your roadmap has specific dates more than two weeks out, you’re not planning; you’re fantasizing. Shipping cultures use “now, next, later” or rolling 6-week cycles. Talking cultures use quarterly commitments that are obsolete the day after they’re published.
  • The “alignment” meeting. If you need a meeting to get aligned, you’re already misaligned. Shipping cultures align by working on the same problem, not by talking about working on the same problem.
  • The PR that sits open for a week. Code review is a blocking call. If your team’s average PR review time is measured in days, you’re not doing code review; you’re doing code bureaucracy. Shipping cultures review within hours, and they optimize for merging, not for perfection.
  • The “we need to do more discovery” stall. Discovery is essential, but it’s also the most common procrastination mechanism in tech. Talking cultures do discovery until they feel confident. Shipping cultures do just enough discovery to ship something that will generate real data, which is infinitely more valuable than confident assumptions.
A team of people collaborating around a whiteboard with sticky notes and diagrams.
Whiteboards are for thinking, not for avoiding the discomfort of shipping.

How to Rewire the System

If you’re leading a team that’s stuck in talk mode, you can’t just demand that people start shipping. The talking is a symptom of the system’s incentives. You have to change the system. Here’s what I’ve seen work, field-tested across multiple teams:

1. Make “shipped” the only status that counts

In your next standup, ban the phrase “working on.” Every update must be phrased as “I shipped X” or “I’m blocked on Y.” If you haven’t shipped anything, your update is “I’m blocked on Z.” This forces the team to surface the real blockers—which are usually not technical, but organizational. The talking culture hides blockers behind activity. The shipping culture treats blockers as the only thing worth discussing.

2. Kill the roadmap, start a “now, next, later” board

Roadmaps with dates are a lie we tell ourselves to feel in control. Replace them with a simple three-column board: Now (this week), Next (next week or two), Later (everything else). The “Now” column must contain only things that can be shipped in a week. If something is too big, break it down until it fits. This forces the team to think in terms of shippable units, not aspirational themes.

3. Institute “Demo Friday” and invite stakeholders

Every Friday, the team demos what they shipped that week. Not what they “worked on”—what they shipped. If there’s nothing to demo, the team sits in awkward silence. This creates a natural consequence for talking without shipping. It also gives stakeholders a visceral understanding of velocity. A slide deck can hide a month of zero progress. A demo cannot.

4. Remove the “code freeze” and “release train”

Code freezes are a sign that your deployment process is too risky. Instead of freezing code, invest in making deployments boring. Feature flags, canary releases, automated rollbacks. A team that can deploy on Friday at 5 p.m. without fear is a team that has actually solved its reliability problems. A team that needs a two-week code freeze before every release hasn’t.

5. Celebrate the small, ugly wins

When someone ships a tiny, imperfect feature that gets even one user to do something new, make a big deal out of it. Publicly. This signals that shipping is valued over polish. Over time, the team will internalize that done is better than perfect, and they’ll start optimizing for done.

The Meta-Lesson: Culture Is a Technical Problem

Engineers love to treat culture as a “soft” problem, something for HR or the “people team” to handle. That’s a category error. Culture is the most technical problem in your organization. It’s a distributed system with emergent behavior, feedback loops, and legacy compatibility issues. If you treat it with the same rigor you’d apply to a production outage, you can debug it. If you treat it as a vague, feel-good initiative, it will rot.

The difference between a startup that ships and one that talks about shipping isn’t talent, funding, or luck. It’s whether the organization has built a system where shipping is the path of least resistance. In a talking culture, shipping is an uphill battle against process, fear, and misaligned incentives. In a shipping culture, talking too much is the uphill battle, because the system is designed to make action easier than deliberation.

So look at your team. What’s the default outcome of a week’s work? If it’s a document, a meeting, or a “decision,” you’re in a talking culture. If it’s a change in production that a real user can interact with, you’re shipping. The good news is that you can refactor a culture just like you refactor a codebase: one small, deliberate change at a time. But you have to start by admitting that the current system is legacy, and it’s time to pay down the debt.

Frequently Asked Questions

How do I know if my team is a talking culture or a shipping culture?

Look at the last two weeks. Count the number of things that reached real users versus the number of meetings, documents, and status updates produced. If the ratio is heavily skewed toward artifacts about the work rather than the work itself, you’re in a talking culture. Another test: ask a random engineer what they shipped this week. If they describe a task they “worked on” rather than a specific, user-visible change, that’s a red flag.

Can a talking culture ever be a good thing?

In regulated industries or safety-critical systems, some level of process and documentation is non-negotiable. But even there, the question is whether the process is proportional to the risk. Most startups aren’t building pacemakers. They’re building SaaS tools, and they’ve adopted process that would be excessive for a nuclear reactor. The goal isn’t zero process; it’s process that serves shipping, not process that replaces it.

What’s the single biggest lever to shift from talking to shipping?

Reduce the batch size. If you can only ship one thing this month, make it the smallest, most embarrassing version of that thing that still teaches you something. The psychological safety to ship small, ugly things is the foundation of a shipping culture. Once the team experiences the feedback loop of real users interacting with their work, the talking starts to feel like the waste it is.

How do I deal with a founder or executive who rewards talking over shipping?

This is the hardest case, because the incentive structure is set from the top. Your best move is to make the cost of talking visible. Start tracking the time between “idea” and “user feedback” for every initiative. Present that data alongside the roadmap. When the CEO sees that the “aligned” project took six months to ship and the “hacky” one took two weeks, you’ve created a data-driven argument for changing the system. If that doesn’t work, you may be in a talking culture that’s too entrenched to fix, and your own career is better served by finding a team that ships.

How to Evaluate a Codebase Before You Join the Team That Owns It

Evaluating a codebase isn’t a technical interview. It’s a systems audit of the engineering culture that produced it. The code is a fossilized record of every decision, panic, and power struggle the team has ever had. If you can read the commit log like a core sample, you’ll see the real architecture: not the one on the whiteboard, but the one that blocks deployments at 4:55 PM on a Friday. This matters because joining a team without reading its code is like marrying into a family without ever meeting them. You’re about to inherit their legacy systems, their race conditions, and their naming conventions. The goal here is to give you a field-tested, opinionated framework for doing a pre-commit code review on a whole organization before you sign the offer letter.

The Commit Log Is the Team’s Group Chat

Forget the README. The README is a marketing document. The commit log is the truth. Start by running git log --oneline --all and scanning the last 300–500 commits. You’re looking for patterns, not individual messages. A healthy log has a rhythm: small, focused commits with clear, imperative messages. An unhealthy log looks like a distributed denial-of-service attack. You’ll see giant monolithic commits with messages like “fix stuff” or “WIP” or, my personal favorite, “.” That single period is a developer screaming into the void.

Pay attention to the ratio of merge commits to actual work. A flood of merge commits from long-lived feature branches is a sign of a team that has optimized for merge conflict resolution as a full-time job. They’re running a blocking call on every integration. Also, look for revert commits. A few reverts are normal. A pattern of reverting the same feature three times in a week is a team that deploys first and asks questions never. They’re running their CI/CD pipeline like a game of roulette.

Close-up of a developer's hands typing on a laptop keyboard, representing the deep inspection of a codebase's commit history.

Dependency Hell Is a Cultural Artifact

Open the package manifest. Whether it’s package.json, requirements.txt, or a pom.xml that looks like it was generated by a slot machine, the dependencies tell you how the team handles risk. A project pinned to specific patch versions from three years ago is a team terrified of change. They’ve been burned by a breaking change in a minor release and now they treat every npm update like a production outage waiting to happen. Conversely, a project floating on latest tags for everything is a team with no fear and, soon, no working build. They’re running a race condition against the entire open-source ecosystem.

Look for the transitive dependency count. If a simple React frontend pulls in 2,100 packages, the team has no supply-chain discipline. They install libraries the way my uncle buys tools at a garage sale: “I might need this one day.” That’s not a codebase; it’s a liability snowball. Check for direct dependencies on abandoned packages. A project that still depends on a library whose last commit was “update copyright year” in 2019 is a project that has no active maintenance culture. The team is building on quicksand and calling it a foundation.

Test Suites Are a Behavioral Interview for the Team

Don’t just check test coverage. Coverage is a vanity metric. A team can achieve 90% coverage with tests that assert true is true and never touch a real edge case. You need to read a sample of the tests. Open the test directory and pick five files at random. Are the tests descriptive? Do they set up realistic scenarios, or do they mock the entire universe until the code is testing a vacuum? A test that mocks every external call is a test that says, “We do not trust our own integrations.” That’s a team that has been burned by flaky environments and decided the solution was to isolate themselves from reality. They’re running a unit test on a simulation of a simulation.

Look for the presence—or absence—of integration and end-to-end tests. A codebase with only unit tests is a team that has never been woken up at 2 AM by a cascading failure in production. They’re still in the innocent phase of their career. A codebase with only end-to-end tests is a team that has given up on fast feedback. They’re running a full system scan because they can’t trust any individual component. The ideal is a layered testing strategy, but what you’re really looking for is evidence that the team has a shared philosophy about quality. If the tests are inconsistent—some files have 100% coverage, others have 0%—you’re looking at a team with internal factions. The testing strategy is a proxy for a civil war.

Two engineers having a tense discussion in front of a whiteboard filled with system diagrams, illustrating the hidden conflicts that codebase patterns reveal.

Configuration Files: The Silent Scream of a Team

Configuration is where good intentions go to die. Open the CI/CD pipeline definition—a .github/workflows file, a Jenkinsfile, a .circleci/config.yml. A healthy pipeline is boring. It lints, tests, builds, and deploys in a predictable sequence. An unhealthy pipeline is a Rube Goldberg machine built out of shell scripts and desperation. Look for hardcoded secrets. If you see an AWS access key in a YAML file, the team has given up on security. They’re one git push away from a cryptocurrency mining operation running on their infrastructure.

Check the environment variable sprawl. A .env.example file with 80 variables is a system that has never been properly documented. Each variable is a silent assumption that will break in a new developer’s local environment. The onboarding process for this team is a week of debugging “it works on my machine” errors. Also, look for feature flags. A codebase littered with feature flags is a team that can’t finish anything. They’re running a permanent beta, afraid to commit to a decision because the product manager changes priorities every sprint. The flags aren’t technical debt; they’re organizational debt encoded in YAML.

Architecture Is a Map of Past Traumas

Skim the directory structure. A monolith with a single utils folder containing 200 files is a team that has never had a difficult conversation about boundaries. They just kept adding to the junk drawer. A microservices repo with 40 services and three contributors is a team that read a blog post about Netflix and decided to replicate it for their internal tooling app. They’re now spending 80% of their time debugging network partitions and serialization errors. The architecture isn’t wrong because it’s a monolith or microservices; it’s wrong because it doesn’t match the team’s size, cognitive load capacity, or operational maturity.

Trace the import graph. If you find circular dependencies, the team has a culture of avoiding hard problems. Circular dependencies are a race condition in the design phase. They happen when nobody wants to say, “We need to stop and refactor this.” Instead, they add another layer of indirection and hope the problem goes away. It won’t. It’ll manifest as a production incident at the worst possible time, probably during a holiday when the on-call engineer is at a family dinner.

Documentation Smells Like Team Rot

Good documentation isn’t about completeness; it’s about freshness. A stale README is worse than no README because it actively lies to you. Check the last commit date on the docs. If the onboarding guide references a version of Node.js that reached end-of-life two years ago, the team has a knowledge transfer problem. They onboard new engineers by throwing them into the deep end and hoping they learn to swim. That’s not mentorship; that’s a hazing ritual.

Look for runbooks. A team without runbooks is a team that reinvents the incident response process every time something breaks. They’ll be in a war room at 3 AM, arguing about who has the credentials to restart the database. A team with runbooks that are kept in a wiki that nobody updates is a team that has accepted failure as a permanent state. They have runbooks for incidents that no longer happen because the service was deprecated six months ago. The documentation is a museum of past disasters.

A cluttered whiteboard with messy diagrams and sticky notes, symbolizing the chaotic documentation and tribal knowledge in a neglected codebase.

Pull Request History: The Team’s Conflict Resolution Style

If you have access to the pull request history, read the comments. This is where you see the team’s real communication patterns. Look for PRs that sat open for weeks with unresolved review comments. That’s a team with a bottleneck—either a reviewer who is a blocker or an author who can’t take feedback. Look for PRs that were merged with failing CI checks. That’s a team that has normalized broken windows. They’ve accepted that the build is red, so they ignore it. The signal-to-noise ratio of their alerting system is zero.

Pay attention to the tone of code reviews. Are comments constructive and specific, or are they passive-aggressive and personal? A comment like “This could be simplified with a map instead of a for loop” is a healthy team. A comment like “Why would you do it this way?” with no further explanation is a team that has stopped collaborating and started resenting each other. The codebase is just the battlefield.

Operational Signals: The Team’s Real SLA

If you can, look at the monitoring dashboards. A team that has no dashboards is flying blind. A team that has dashboards but no alerts is in denial. A team that has alerts but they’re all silenced or ignored is in a state of learned helplessness. The most dangerous signal is a dashboard that’s always green. That doesn’t mean the system is healthy; it means the monitoring is broken. Real systems have fluctuations. A flat line is a lie.

Check the error logs. Not the aggregated, sanitized logs in the observability platform—the raw logs from a production instance. If you see the same NullPointerException scrolling by every second for the last six months, the team has accepted a baseline level of failure. They’ve redefined “working” to mean “not completely on fire.” That’s a cultural decision, not a technical one.

What You’re Actually Evaluating

You’re not evaluating the code. You’re evaluating the team’s relationship with their own work. A codebase that’s a mess but has a clear, recent history of refactoring is a team that has woken up and started fixing things. That’s a good sign. A codebase that’s clean but hasn’t been touched in a year is a team that has stopped building. That’s a dead project. The worst codebase is the one where every file has a different style, different patterns, and different authors who have all left the company. That’s a team with no collective ownership, just a series of contractors who did their time and escaped.

Your goal is to find the team’s technical debt ceiling—the point at which the accumulated mess makes forward progress impossible. Some teams operate comfortably at 80% of that ceiling. Others are at 110% and have been in emergency mode for so long they think it’s normal. You want to join a team that’s aware of its debt, has a plan to manage it, and has the organizational power to execute that plan. If you can’t find evidence of that, you’re walking into a legacy system where the legacy is the team itself.

Frequently Asked Questions

What if I don’t have access to the repository before I join?

Ask for a read-only guest account or a walkthrough with a senior engineer sharing their screen. If the company refuses, treat that refusal as a data point. A team that won’t let you see the code before you accept an offer is a team that knows what you’ll find. They’re hiding a blocking call in their hiring process. You can also ask for a take-home exercise that involves reviewing a real piece of their codebase—sanitized if necessary. How they respond tells you more than the code itself.

What’s the single most reliable indicator of a healthy codebase?

Consistency. Not perfection, not 100% test coverage, not the latest framework. A codebase where similar problems are solved in similar ways across the board is a codebase owned by a team that has agreed on standards and enforces them. That agreement is a social contract. It means the team has had the hard conversations about conventions and tradeoffs. A codebase where every file looks like it was written by a different person in a different decade is a team that has never had those conversations—or had them and lost.

How much time should I spend on this evaluation?

Two to three hours, focused. You’re not doing a full code review; you’re doing a cultural biopsy. Spend 30 minutes on the commit log, 30 minutes on the dependency manifest and configuration, 30 minutes on tests, 30 minutes on documentation and PR history, and the remaining time on architecture and operational signals. If you can’t form a clear picture in that time, the codebase is too chaotic to evaluate quickly—which is itself a signal. A well-organized codebase reveals its patterns quickly. A messy one resists understanding because there’s no pattern to find.

What if the codebase is legacy and the team knows it?

That’s the best-case scenario for a messy codebase. Ask them directly: “What’s your plan for this?” A team that can articulate a clear, incremental modernization strategy—with specific milestones and a realistic timeline—is a team you can join. A team that says “we’ll rewrite it someday” is a team in denial. A rewrite is the nuclear option, and nuclear options are usually a sign that the team has lost the ability to make incremental improvements. You want to see evidence of the Strangler Fig pattern in action: small, safe replacements that gradually consume the old system.

The Decision Framework

After your evaluation, you should be able to answer three questions. First, does the team treat their codebase as a product or a cost center? A product team invests in maintainability, testing, and documentation because they know the codebase is their primary asset. A cost-center team treats the codebase as a necessary evil, minimizing investment until it becomes a crisis. Second, is the team’s pace sustainable, or are they running on adrenaline? A team that’s always in crunch mode is a team that has normalized heroics. You’ll be expected to be a hero too, and heroes burn out. Third, does the team have the authority to fix what’s broken, or are they constantly overruled by product managers or architects who don’t touch the code? A team without agency is a team that will make you feel powerless.

If the answers are “cost center,” “adrenaline,” and “no agency,” walk away. No salary is worth inheriting a legacy system where the legacy is the culture. If the answers are mixed, you have a negotiation point. You can join and be part of the solution—but only if the team is ready to change. The codebase will tell you the truth. You just have to listen.

How to Evaluate a Codebase Before You Join the Team (and Why Most Engineers Skip the Hard Parts)

You’re about to accept an offer. The money’s decent, the title sparkles, and the recruiter said “impact” at least seven times. But you haven’t seen the codebase yet—or worse, you’ve seen it and didn’t know what to look for. A codebase is a fossilized record of every management failure, every rushed deadline, and every architectural tantrum the team has ever thrown. Most engineers evaluate a codebase like a tourist: they glance at the README, check if it uses React, and call it a day. That’s how you end up maintaining a monolith held together by shame and cron jobs. This article gives you a field-tested method for reading a codebase like a detective, so you can spot the organizational rot before you’re part of it.

Engineer staring at messy whiteboard diagrams, representing the confusion of inheriting a poorly documented codebase
Messy whiteboards are a warning sign, not a design philosophy. (Photo by fauxels via Pexels)

The Codebase Is a Confession, Not a Resume

Every team will tell you they’re refactoring the legacy monolith into microservices. The codebase tells you they’ve been saying that since 2018 and the only thing that’s been “extracted” is a single utility function for formatting dates. When you evaluate a codebase, you’re not judging technical quality in a vacuum. You’re reverse-engineering the team’s actual priorities, their fear of deployment, and the unspoken truces they’ve made with technical debt. A codebase with zero tests isn’t a sign of lazy developers—it’s a sign of a management culture that has never given anyone time to write them, or worse, punishes the slowdown that quality requires.

Start with the Git Log, Not the Source Code

Before you read a single line of code, pull the commit history for the last six months. Look for patterns that reveal the team’s nervous system:

  • Commit message quality. Are they descriptive (“Fix race condition in user session renewal”) or performative (“cleanup”)? A history full of “wip” and “fix” suggests a team that treats version control as a backup tool, not a communication tool.
  • Deployment fear. Do you see clusters of commits at 6 PM on Fridays? That’s a team that deploys and prays. Do you see a flurry of reverts on Monday mornings? That’s a team that deploys, prays, and then panics.
  • Hero patterns. If 70% of commits come from one person, you’re looking at a bus factor of one. That person is probably exhausted, resentful, and actively looking for someone to hand the pager to—maybe you.
  • Dependency drift. Check how often third-party libraries are updated. A codebase stuck on a three-year-old version of a framework isn’t “stable”—it’s abandoned. The upgrade will be your problem, and it will be a nightmare.

Architecture Archaeology: What the Structure Actually Says

Every codebase has an intended architecture (the diagrams in Confluence) and an actual architecture (the folder structure and import graphs). The gap between them is where your future suffering lives. Use a tool like dependency-cruiser or even a simple grep to trace imports between modules. If the utils folder imports from core, which imports from utils, you’ve found a circular dependency that the team has learned to work around rather than fix. That’s not a technical problem—that’s a cultural one.

Two engineers pointing at a screen, one looking confused, representing the gap between documented and actual architecture
If the architecture diagram and the code tell different stories, believe the code. (Photo by fauxels via Pexels)

The Test Suite as a Cultural Artifact

Don’t just check test coverage—coverage numbers are easy to game. Run the test suite yourself. How long does it take? A suite that takes 45 minutes to run is a suite nobody runs before pushing. Look at what the tests actually test. If you see tests with names like test_process that mock every external service and assert that a function returns True, you’re looking at tests written to satisfy a coverage gate, not to prevent regressions. That’s a team that has weaponized testing against itself.

Pay attention to the test data. Is it realistic, or is it full of “John Doe” and “test@example.com”? Realistic test data means the team has spent time thinking about edge cases. Placeholder data means they’ve spent time thinking about how to make the CI pipeline green with minimal effort.

Ask Questions That Reveal the Unwritten Rules

During the interview, you’ll get sanitized answers. The real evaluation happens when you ask specific, uncomfortable questions and watch the reactions. Don’t ask “How do you handle technical debt?” Ask “When was the last time you stopped feature work to pay down debt, and how did management react?” If the answer is a long pause followed by “Well, it’s a balance,” you’ve found a team that never wins that argument.

Ask to see the on-call rotation and the incident history. A healthy team has a manageable number of incidents and blameless postmortems that actually identify systemic causes. A dysfunctional team has a pager that goes off constantly and postmortems that all end with “Action item: add more monitoring.” That’s not a solution—that’s a ritual to make people feel better about a system they can’t fix.

The Dependency Diet: What the Codebase Consumes

Open the package manifest and look for these red flags:

  • Orphaned packages. Libraries that haven’t been updated in years, maintained by a single person in Nebraska who might have moved on to farming.
  • Trivial dependencies. A package imported just to check if a number is even. That’s a team that doesn’t understand the supply chain risk they’re carrying.
  • Duplicate functionality. Two different date-handling libraries. This happens when teams don’t talk to each other, or when one team passive-aggressively introduces a new dependency to avoid collaborating on the existing one.

Each of these is a small crack in the team’s decision-making process. Individually, they’re fixable. Together, they’re a billboard advertising that nobody is minding the store.

Close-up of tangled network cables, representing messy code dependencies
Your dependency graph shouldn’t look like this. (Photo by cottonbro via Pexels)

The Pull Request History Is a Window into Team Dynamics

If you can get access to the pull request history, read it like a novel. Look at the comments. Are they substantive (“This could cause a race condition with the caching layer”) or performative (“Nit: missing semicolon”)? A team that only nitpicks style is a team that’s given up on meaningful code review. Look at how long PRs sit open. If the average time-to-merge is measured in days, not hours, the team has a bottleneck—either a gatekeeper who insists on reviewing everything, or a culture where nobody feels responsible for unblocking their colleagues.

Also check who merges PRs. If every PR is merged by the same two senior engineers, the team isn’t mentoring. They’re hoarding context. When those two people leave—and they will—the codebase becomes a haunted house where nobody knows why the lights flicker.

Documentation: The Canary in the Knowledge Silo

Documentation isn’t about having a perfect wiki. It’s about whether the team has a habit of writing things down. Look for runbooks that explain how to deploy, how to handle common incidents, and how the data flows. If the runbook says “Ask Dave,” you’ve found a single point of failure. If there is no runbook, Dave already left, and the team is just hoping nothing breaks.

Check the README for a section on local development setup. If it takes you more than 15 minutes to get the project running on your machine, the team has normalized an unacceptable onboarding tax. That tax is paid by every new hire in lost productivity and quiet frustration. You’ll be paying it too.

Build and Deploy: The Truth About Operations

Ask to see the CI/CD pipeline configuration. A healthy pipeline has fast feedback loops: linting and unit tests run in under 10 minutes, integration tests are parallelized, and deployments are automated but gated. A sick pipeline has a single 90-minute job that runs everything sequentially and fails on flaky tests that everyone ignores. If the team’s response to a flaky test is “just re-run it,” they’ve accepted unreliability as normal. That acceptance will seep into every part of their engineering practice.

Ask about the deployment process. If it involves a “deployment checklist” that a human follows, the team doesn’t trust their automation—or their automation doesn’t exist. Manual deployments are a sign that the team has been burned too many times and has responded by adding process instead of fixing the underlying fragility.

Security Posture: What They’re Afraid to Tell You

You don’t need to be a security expert to spot basic hygiene failures. Check if the codebase has hardcoded secrets. Grep for password, secret, token, and common cloud provider key patterns. If you find any, the team’s security practice is “hope nobody looks.” Ask about their dependency vulnerability scanning. If they don’t have an automated tool like Dependabot or Snyk running, they’re flying blind. Ask when their last security incident was and what changed afterward. A team that says “We’ve never had one” is either lying or hasn’t looked hard enough.

FAQ: The Questions You’re Afraid to Ask Out Loud

What if I’m not allowed to see the codebase before accepting?

That’s a red flag the size of a production outage. Any team that won’t let a serious candidate review the code—even under NDA—is hiding something. It might be a proprietary concern, but more often it’s embarrassment. Walk away unless the compensation is life-changing, and even then, price in the misery.

How do I evaluate a codebase if I’m not an expert in the language or framework?

You don’t need to be a language expert to spot structural problems. Focus on the signals that are language-agnostic: commit history patterns, test suite runtime, dependency freshness, documentation quality, and the team’s answers to operational questions. Bad engineering culture transcends syntax. If the team can’t explain their architecture clearly to a competent outsider, they don’t understand it themselves.

What’s the single most damning thing to find in a codebase?

A file called utils.py (or helpers.js, misc.go, etc.) that’s more than 500 lines long and imported everywhere. That’s not a utility module—that’s a graveyard for code the team was afraid to put in the right place. It means the team lacks the will or the authority to enforce architectural boundaries. Every feature you build will add to that graveyard until the whole system collapses under its own weight.

How do I bring up these concerns without sounding like a jerk in the interview?

Frame your observations as curiosity, not criticism. Instead of “Your test coverage is terrible,” try “I noticed the test suite is mostly integration tests. How does the team balance integration and unit testing, and what’s been the historical tradeoff?” You’re not there to judge—you’re there to understand if you can work within their reality. If they get defensive about honest technical questions, that’s your answer.

Making the Decision: What’s Fixable and What’s Forever

No codebase is perfect. The question isn’t whether there are problems—it’s whether the team has the capacity and the mandate to fix them. A codebase with technical debt but a team that openly acknowledges it and has a plan is a better bet than a “clean” codebase maintained by people who insist everything is fine. The former is a team you can learn from. The latter is a team that will gaslight you when things break.

Before you sign, ask yourself: Is this a codebase I can influence, or one that will slowly wear me down? If the team’s response to every problem is “We’ve always done it this way,” you’re not joining an engineering team. You’re joining a support group for people who’ve given up.

Why Most Engineering Teams Need Fewer Meetings and More Asynchronous Communication

You know the feeling. You’re 30 minutes into a daily standup that was supposed to take 15, and someone is narrating their Jira board like it’s a bedtime story. It’s not boredom that gets you. It’s the slow, creeping dread that your team’s most expensive resource—uninterrupted time to actually think—is being carved up by the very processes that were supposed to help. Meetings have become the default coordination mechanism in software engineering, and it’s a train wreck. Not because meetings are inherently evil, but because they’ve metastasized into a synchronous, real-time addiction that punishes deep work and rewards performative busyness. The alternative isn’t chaos. It’s asynchronous communication: a deliberate, written-first culture where information flows without dragging everyone into the same room at the same time. This isn’t a productivity hack. It’s a structural fix for a broken operating model.

Engineers collaborating around a whiteboard with sticky notes and diagrams

Engineering teams are complex systems. When you treat coordination as a series of live, synchronous events, you introduce coupling that rivals the worst spaghetti code. Every meeting creates a dependency: you can’t start work until the meeting happens, you can’t make decisions without the meeting, and you can’t share context without yanking people away from their actual work. The result is a team that moves at the speed of its calendar, not its capability. Asynchronous communication decouples these dependencies. It lets information travel at the speed of reading, not the speed of scheduling. And it forces a discipline that most teams desperately need: the ability to write clearly, think before responding, and treat other people’s attention as the finite, non-renewable resource it is.

The Real Cost of Synchronous-By-Default

Let’s talk numbers, because engineers respect data. A single one-hour meeting with six engineers doesn’t cost one hour. It costs six hours of focused work, plus the cognitive switching costs before and after. Research on task switching shows it can take over 23 minutes to regain deep focus after an interruption. If your team has a morning standup, a mid-day sync, and an afternoon review, you’ve effectively capped anyone’s ability to do deep work at zero. You’re paying senior engineers to context-switch like a help desk, and that’s a terrible return on investment.

The damage goes deeper than lost time. Synchronous communication favors the loud, the quick-witted, and the native English speakers. It punishes introverts, remote workers in distant time zones, and anyone who needs time to process information before forming an opinion. Your meeting-heavy culture isn’t just inefficient; it’s exclusionary. You’re optimizing for consensus theater while the real thinking happens in the margins—or doesn’t happen at all because nobody has the uninterrupted blocks to do it.

How Meetings Become Organizational Scar Tissue

In code, we talk about technical debt: the accumulated shortcuts that slow down future development. Meetings are organizational debt. They’re added as a quick fix—“let’s sync on this”—and then never removed. Over time, they calcify into rituals that nobody questions. The weekly status meeting that started during a crisis three years ago? Still on the calendar. The cross-team alignment sync that was supposed to be temporary? Now it’s a recurring series with 15 attendees and no agenda. This is the organizational equivalent of a function with 47 parameters. It grew without design, and now it’s too politically dangerous to refactor.

Asynchronous communication forces you to confront this debt. When you can’t rely on a meeting to disseminate information, you have to write it down. That written record becomes searchable, linkable, and referenceable. It doesn’t disappear when someone leaves the company. It doesn’t require repeating the same update five times for five different audiences. It’s a single source of truth that scales horizontally, while meetings scale vertically—and poorly.

What Asynchronous Communication Actually Looks Like

This isn’t about replacing meetings with Slack. If you simply move the same chaotic, real-time chatter from a conference room to a chat app, you’ve accomplished nothing. True asynchronous communication is structured, written, and designed for consumption on the reader’s schedule, not the writer’s. It uses tools like long-form documents (design docs, RFCs, decision records), recorded video updates (Loom, short screencasts), and project management systems that track work status without requiring a verbal handoff.

Person typing on laptop with notebook and coffee, representing focused asynchronous work

The key shift is from “let’s meet to discuss” to “here’s a document, please comment by Thursday.” This respects the maker’s schedule, a concept Paul Graham articulated years ago that most companies still ignore. Makers—engineers, designers, writers—need long, uninterrupted blocks to produce value. Managers can operate on a manager’s schedule, chopped into one-hour slots. When you force makers onto a manager’s schedule, you get a team that’s great at attending meetings and terrible at shipping software.

Writing as a Core Engineering Skill

Here’s a hard truth: if your engineers can’t write clearly, your team has a communication problem that no number of meetings will fix. Asynchronous culture demands writing. Design documents, architecture decision records (ADRs), postmortems, project proposals—these are the artifacts of a healthy engineering organization. They force clarity. You can’t hand-wave through a written argument the way you can in a verbal discussion. If your reasoning is flawed, it shows up on the page. If your assumptions are unstated, they become visible gaps.

This is why companies like Amazon have institutionalized the six-page narrative memo. It’s not about bureaucracy. It’s about forcing rigorous thinking. A well-written document is a thinking tool, not just a communication tool. It lets the entire team examine the logic, poke at the edges, and build on the ideas without the time pressure of a live meeting. The comments and revisions become a permanent record of the decision-making process, which is invaluable when someone asks six months later, “Why did we build it this way?”

Identifying the Meetings You Should Kill First

Not all meetings are evil. Some require real-time collaboration: brainstorming sessions, complex technical discussions with multiple stakeholders, sensitive one-on-ones. The problem is that most recurring meetings don’t fall into these categories. They’re status updates, information broadcasts, and coordination overhead disguised as collaboration. Here’s a simple diagnostic: if the meeting could be replaced by an email, a document, or a five-minute video without losing fidelity, it should be.

Start with the daily standup. In most teams, it’s become a status report for the manager, not a coordination tool for the team. Replace it with a written check-in in your project management tool or a dedicated Slack channel. Each person posts what they worked on yesterday, what they’re working on today, and any blockers. The team reads it on their own time. If someone has a blocker, they can raise it immediately in a dedicated channel or schedule a focused conversation with the relevant person. You’ve just saved 30 minutes per person per day and eliminated the most common meeting complaint: “This could have been an email.”

Next, audit your recurring calendar. For every standing meeting, ask: what’s the output? If the answer is “alignment” or “visibility,” you have a process problem, not a meeting problem. Alignment comes from shared understanding, which comes from shared documents. Visibility comes from transparent work tracking, not from verbal updates. Kill the meeting and build the system that makes it unnecessary.

The Tools That Enable Async, Not Just More Noise

Tool choice matters, but it’s secondary to culture. You can’t Slack your way to asynchronous nirvana. That said, certain tools lower the friction. Long-form writing tools like Notion, Confluence, or Google Docs work for collaborative documents. Loom or other screen recording tools let you share complex ideas with visuals without scheduling a call. Project management tools like Linear, Jira, or Asana track work status so you don’t need a meeting to know what’s happening. The common thread: they all allow consumption on the reader’s schedule.

Version control platforms like GitHub or GitLab are inherently asynchronous. Pull requests, code reviews, and issue discussions happen without real-time coordination. This is the model to extend to the rest of your work. If your code collaboration is async but your planning and decision-making are synchronous, you’ve got an impedance mismatch that creates constant friction.

When Async Fails: The Edge Cases

Async isn’t a silver bullet. There are situations where synchronous communication is genuinely necessary. Complex technical discussions with multiple experts often benefit from real-time back-and-forth. Crisis situations demand immediate coordination. Relationship-building and trust formation can be harder without some face-to-face interaction. The goal isn’t to eliminate meetings entirely; it’s to make them the exception, not the rule.

The danger is when teams use these edge cases to justify the status quo. “We need our daily standup because sometimes there are blockers” is a failure of process design. If blockers only surface during a scheduled meeting, you have a deeper problem: people aren’t raising issues when they occur. Fix that cultural problem directly. Create a norm that blockers are escalated immediately, not saved for the next day’s meeting. The meeting is a bandage, not a cure.

Team collaborating around a table with laptops and documents, showing a focused working session

Remote and Distributed Teams: Async Is Non-Negotiable

If your team spans time zones, synchronous-by-default is actively harmful. You’re forcing some team members to attend meetings at 6 a.m. or 10 p.m. regularly. This isn’t just inconvenient; it’s a fast track to burnout and attrition. Async communication is the only scalable way to include people across time zones without creating second-class team members. It also forces you to document decisions and context, which benefits everyone, including future hires who weren’t in the room when decisions were made.

Many companies claim to be remote-friendly while maintaining a synchronous culture centered on one time zone. That’s not remote-friendly. That’s office culture with a longer commute. True remote work requires rethinking how information flows, and async is the foundation. Without it, you’re just replicating the dysfunctions of a co-located team across Slack and Zoom, and the results are predictably worse.

Implementing Async Without Causing a Revolt

Shifting a team from synchronous to asynchronous communication is a cultural change, and cultural change is hard. If you announce on Friday that all meetings are canceled starting Monday, you’ll get chaos and resentment. Start with a pilot. Pick one recurring meeting—the daily standup is the easiest target—and replace it with a written process for two weeks. Gather feedback. Tweak the format. Show the team the time savings and the quality of information sharing. Let the results speak for themselves.

Invest in writing skills. Many engineers are terrible writers because nobody ever taught them and the culture never demanded it. Provide templates for async updates. Give feedback on clarity and conciseness. Make writing a valued skill in performance reviews. When people see that clear writing leads to fewer interruptions and more autonomy, they’ll adopt it willingly. When they see that verbose, unclear writing leads to more meetings to “clarify,” they’ll improve or they’ll self-select out.

Leadership’s Role in Modeling Async

This fails if leadership doesn’t model it. If the CTO sends a Slack message saying “let’s jump on a quick call” every time there’s a question, the culture won’t change. Leaders must write. They must share written updates, make decisions in documents, and respect their team’s focus time. They must stop using “I’m a verbal processor” as an excuse to hijack other people’s schedules. Verbal processing is fine—do it in a document, record a video, or talk to yourself. Don’t make it your team’s problem.

Leaders also need to protect the async experiment from organizational gravity. Other departments, other executives, and external stakeholders will still want meetings. That’s okay. The goal isn’t to refuse all meetings; it’s to make your team’s default communication mode async. When a meeting request comes in, the first question should be: “Can we handle this with a document first?” Over time, this shifts the entire organization’s expectations.

Measuring the Impact: What Improves When Meetings Die

You’ll know it’s working when you see these signals: pull requests get reviewed faster because people have uninterrupted time. On-call incidents get resolved more smoothly because documentation is current and searchable. New hires ramp up quicker because they can read the decision history instead of relying on tribal knowledge. Team satisfaction scores improve because people feel in control of their time. Shipping velocity increases not because people are working harder, but because they’re working with fewer interruptions.

One counterintuitive benefit: meetings that survive the purge become much better. When you reduce the total meeting load, the remaining synchronous time is treated as precious. People come prepared. Discussions are focused. Decisions actually get made. You’ve eliminated the organizational filler and kept the substance. That’s a meeting culture worth having.

Frequently Asked Questions

How do you handle urgent issues without real-time meetings?

Urgent issues should be rare in a well-run engineering team. If everything is urgent, you have a prioritization problem, not a communication problem. For genuine emergencies—production outages, security incidents—real-time coordination is appropriate. Define clear escalation paths: a dedicated incident channel, an on-call rotation, a war room protocol. The key is that these are exceptions, not the daily operating model. If you’re using “urgent” to justify constant interruptions, you’re misusing the word.

Won’t async communication slow down decision-making?

It depends on what you mean by “slow.” If you measure decision speed by how quickly a meeting can be scheduled, async might seem slower. But if you measure by how quickly a well-reasoned, documented decision is made and communicated to everyone who needs to know, async is often faster. Synchronous decisions are fast to make and slow to propagate. Async decisions take slightly longer to form but are instantly available to the entire organization. Over time, the async approach compounds: decisions build on each other, context accumulates, and the team gets faster because it’s not constantly reinventing understanding.

What about team bonding and culture? Don’t we need meetings for that?

Team bonding is important, but it doesn’t require status meetings. Separate social connection from work coordination. Schedule optional social events, virtual coffee chats, or in-person offsites for relationship-building. These are valuable and should be protected. But don’t confuse them with the daily coordination machinery. A daily standup is a terrible social event, and a team happy hour is a terrible place to discuss project blockers. Keep the purposes distinct, and you’ll do both better.

How do we handle performance feedback and sensitive conversations?

One-on-ones, performance reviews, and sensitive feedback conversations are inherently synchronous. They require real-time, private discussion. These meetings should stay. The goal isn’t to eliminate all synchronous communication; it’s to eliminate the low-value, recurring, coordination-overhead meetings that consume the bulk of the team’s time. Protect the high-value synchronous interactions by clearing out the noise around them.

The shift to asynchronous communication isn’t a productivity trend. It’s a recognition that software engineering is a thinking profession, and thinking requires uninterrupted time. Every meeting you eliminate is an investment in your team’s ability to do the work they were hired to do. Start small, measure the results, and let the data make the case. Your team’s code—and their sanity—will thank you.

How to Evaluate a Codebase Before You Join the Team

Evaluating a codebase before you sign the offer isn’t about grading the code. It’s about diagnosing the team. A repo is a fossil record of every management misfire, every stupid deadline, and every architecture argument someone lost in a conference room. The code itself is rarely the villain. The patterns of neglect, over-engineering, and sheer terror frozen into the directory structure—those are what will grind you down. I call this organizational archaeology. Treat the codebase as an artifact of the engineering culture, and you’ll spot the red flags no hiring manager will ever mention. If you’re a senior engineer, a staff engineer, or a CTO walking into a turnaround, this is your due diligence. It’s the difference between joining a team that builds and a team that just patches.

The Commit Log as a Cultural Autopsy

Forget the README. The commit history is the most honest document in the repository. It tells you who works weekends, who owns what, and whether the team treats version control as a communication tool or a backup script. Start by running git log --format='%an' | sort | uniq -c | sort -nr. If 80% of the commits come from one person, you’re not joining a team. You’re joining a single point of failure with a supporting cast. That person is probably burned out, overly critical in code reviews, and has veto power over every technical decision because they hold all the context in their head.

Next, look at the commit messages. Are they descriptive, or do you see a wall of “fix,” “update,” and “wip”? A team that writes lazy commit messages writes lazy code. They’re not thinking about the maintainer, which means they’re not thinking about you. One particularly damning pattern is the “megacommit”—a single commit that touches 40 files across the UI, API, and database layers. This is a sign of a team that doesn’t understand separation of concerns or is forced to ship features in a panic. Either way, it means the codebase is a monolith in practice, even if it’s split into microservices on paper.

The Blame Game

Use git blame on the files that change most frequently. If you see the same few names on every hot file, you’ve found the team’s bottlenecks. More importantly, look for files where the original author is long gone and the current maintainers are just adding patches on top of patches. These are the “haunted forests” of the codebase—areas everyone is afraid to refactor because nobody understands the original intent. A healthy team has a rotation of names on every file. A sick team has a graveyard.

The Dependency Manifesto

Open the package manager file—package.json, requirements.txt, go.mod, whatever. This is a political document. It tells you how the team balances stability against novelty, and how much they trust external maintainers. First, check the version pinning. If every dependency is pinned to an exact patch version, the team has been burned by a breaking change in the past and now trusts no one. That’s not necessarily bad—it’s a scar that tells a story. But if dependencies are unpinned and the project is more than a year old, you’re looking at a team that doesn’t know what reproducibility means.

Count the number of dependencies. A typical web application should not need 1,500 npm packages to render a form. A bloated dependency tree is a sign of architectural laziness—pulling in a library for a three-line function because nobody wanted to write a utility module. It’s also a security nightmare. Check for abandoned packages. Run npm outdated or the equivalent. If you see packages that are two major versions behind, ask about it in the interview. If they say “we’ve been meaning to upgrade but it’s not a priority,” what they mean is “we do not invest in maintenance, and you will be the one to fix it when it breaks.”

Forking and the “Not Invented Here” Syndrome

Look for internal forks of public packages. A team that maintains its own fork of a common library has either a very good reason or a very bad case of control-freak syndrome. If the fork exists because the team needed a specific patch and contributed it upstream, that’s a sign of maturity. If the fork exists because “we didn’t like the direction of the project” and it’s now 200 commits behind upstream, you’re looking at a team that builds silos. They’ll build one around you too.

Testing: The Team’s True Religion

Don’t ask “do you write tests?” Every team says yes. Instead, look at the test files. Run the test suite and watch the output. If the tests take 45 minutes to run and half of them are skipped, the team has given up on testing as a practice. They keep the suite around like a gym membership—it makes them feel better, but they never use it. Check the coverage reports, but don’t obsess over the percentage. 90% coverage can be a lie if the tests are just calling functions without assertions. Look at the quality of the tests. Are there tests that mock the entire universe and test nothing but the mocking framework? That’s a team that values ceremony over safety.

One of my favorite tricks is to look for the oldest test in the suite. If it’s a brittle, end-to-end test that breaks on every third commit and has been commented out and uncommented six times, you’ve found the team’s collective trauma. That test represents a production outage from three years ago that nobody has had the courage to properly fix. The team’s entire deployment process is probably built around placating that one test.

The Flaky Test Graveyard

Search the codebase for keywords like “skip,” “ignore,” “flaky,” “TODO,” and “FIXME.” A healthy codebase has a few of these, clustered around areas of active development. A sick codebase has hundreds, scattered like confetti, some dating back to the initial commit. Each one is a broken promise. The team has learned to live with them, which means they’ve learned to live with broken windows. That attitude will infect every other engineering practice.

Architecture as Organizational Chart

Conway’s Law isn’t a theory; it’s a physical law of software. The codebase structure will mirror the communication structure of the team that built it. If the codebase is split into perfectly decoupled microservices but the team is 12 people sitting in one room, someone has been reading too many blog posts. That architecture will crumble the first time a cross-cutting change is needed, because the organizational boundaries don’t match the service boundaries. Conversely, if the codebase is a monolith but the team is split into three squads on different continents, you’ll spend half your time in merge conflict resolution.

Look at the directory structure. Does it reflect the domain, or does it reflect the framework? A codebase organized by technical role—controllers/, models/, views/—is a sign of a team that thinks in terms of technology, not in terms of the problem they’re solving. A codebase organized by feature or domain—invoicing/, user-management/, search/—is a sign of a team that understands the business. The former is easy to navigate if you know the framework; the latter is easy to navigate if you know the product. Which one do you want to learn?

The Shared Kernel Anti-Pattern

Watch out for a common/ or shared/ or utils/ directory that everything depends on and nobody owns. This is the tragedy of the commons in code form. It becomes a dumping ground for functions that two teams needed but neither wanted to maintain. Over time, it accretes circular dependencies and becomes impossible to refactor. If you see a utils.js with 2,000 lines and no tests, run.

Documentation: The Lies We Tell Ourselves

Every team has a README.md. Most of them are fiction. The real documentation is in the Slack channel, the wiki that nobody updates, and the tribal knowledge stored in the tech lead’s head. But you can still learn from the artifacts. Check the last commit date on the documentation files. If the CONTRIBUTING.md was last touched two years ago and still references a branching strategy the team abandoned, you’re looking at a team that doesn’t onboard well. You’ll be expected to “figure it out.”

Look for architecture decision records (ADRs). These are short documents explaining why a particular technical decision was made. If the team has a directory full of ADRs, they’re thoughtful and intentional. If they have none, decisions are made in hallways and forgotten. Ask in the interview: “Can you show me an example of a recent ADR?” If they blink at you, that’s your answer.

Deployment and Operations Scars

The CI/CD pipeline is the team’s nervous system. If it’s slow, flaky, and requires manual intervention, the team is operating in a constant state of low-grade stress. Look at the build history. A pipeline that fails on every other commit and requires a “re-run” is not a pipeline; it’s a suggestion. Check the deployment frequency. If the team deploys once a month and it’s a big ceremony with a rollback plan and a war room, they don’t trust their own code. That means they don’t have good testing, good monitoring, or good rollback mechanisms. You’ll spend your first six months just making deployments boring again.

Look at the infrastructure-as-code files. If the production configuration is in a separate, private repository that only the ops team can see, you’re joining a team with a wall between developers and operations. That wall is where responsibility goes to die. You’ll write code, throw it over the wall, and hope it works in production. When it doesn’t, you won’t have the access to debug it.

Monitoring and Alerting

Ask to see the production dashboards. If the team doesn’t have dashboards, or if the dashboards are all green because the alerting thresholds are set to “never page,” you’re joining a team that flies blind. Look for the on-call rotation. If there’s no rotation, or if the same person has been on call for six months, the team doesn’t take operational responsibility seriously. That person is a martyr, and you’ll be expected to join the religion.

How to Ask About What You Find

You now have a list of red flags. Don’t walk into the interview and recite them like a prosecutor. That’ll just make people defensive. Instead, frame your questions as curiosity. “I noticed the test suite takes about 30 minutes to run—how does that affect your development workflow?” If they say “oh, we just run the tests locally and only run the full suite in CI,” they’ve acknowledged the problem and have a workaround. If they say “it’s fine, we’re used to it,” they’ve normalized the pain. That’s a cultural problem, not a technical one.

Ask about the last time they deleted a significant amount of code. A team that never deletes code is a team that’s afraid of their own codebase. They’re hoarders. Ask about the worst bug they shipped in the last year. If they can’t think of one, they’re either not monitoring production or they’re lying. If they tell you a story with a beginning, middle, and end—including the postmortem and the changes they made—you’re talking to a mature team that learns from failure.

FAQ

What if I don’t have access to the codebase before I join?

Many companies won’t give you repository access until your first day. That’s normal. But you can still ask for a guided tour during the interview process. Ask the hiring manager: “Can one of the engineers walk me through the codebase structure and the CI/CD pipeline?” If they refuse, that’s a red flag in itself. If they agree, you can ask the same diagnostic questions while screen-sharing. Pay attention to how the engineer navigates the code. Do they know where things are, or are they searching and guessing? That tells you how well the team shares context.

What’s the single biggest red flag in a codebase?

A lack of tests combined with a lack of documentation. Either one alone is survivable. You can add tests to undocumented code, or you can document untested code. But if neither exists, the team is operating on oral tradition alone. That means the codebase is a black box, and the only way to understand it is to ask the elders. If the elders leave—and they will—you’re left with a haunted forest and no map. This is the most common failure pattern I see in startups that grew too fast.

How do I evaluate a codebase that’s being rewritten?

Rewrites are the nuclear option of software engineering, and they usually fail. If the team is actively rewriting the codebase, ask why. If the answer is “the old code is a mess and we want to start fresh,” you’re joining a team that doesn’t understand the business logic embedded in that mess. The rewrite will take three times longer than estimated and will miss critical edge cases that the old system handled. A better answer is: “We’re incrementally replacing specific modules because the old architecture can’t scale for reason X.” That shows they understand the tradeoffs and aren’t just chasing technical vanity.

What tools can I use to automate this evaluation?

Static analysis tools can give you a quick health check. cloc counts lines of code and breaks them down by language, which helps you understand the codebase composition. dependency-cruiser visualizes dependency graphs and can spot circular dependencies. code-maat is a command-line tool that mines commit history for patterns like change frequency, author churn, and hotspots. These tools don’t replace human judgment, but they can confirm or refute your gut feelings with data. If you want to go deeper, Adam Tornhill’s work on code as a crime scene is the definitive resource on mining software repositories for organizational insights.

The Decision Framework

After you’ve done your archaeology, you need to make a decision. I use a simple framework: separate the problems into “technical debt” and “cultural debt.” Technical debt is fixable. You can refactor a monolith, add tests, upgrade dependencies. Cultural debt is not fixable by you. If the team doesn’t value testing, doesn’t write documentation, doesn’t do postmortems, and doesn’t delete dead code, you won’t change that as a new hire. You’ll either burn out fighting it or assimilate and become part of the problem.

Your goal isn’t to find a perfect codebase. Perfect codebases don’t exist. Your goal is to find a team whose problems you’re willing to live with. Every codebase has scars. The question is whether the team is healing those scars or just hiding them under bandages. Join a team that debrides its wounds. Avoid a team that pretends they’re not bleeding.

This is the first article in what I plan to make a recurring column: Organizational Archaeology. Next up: how to run a codebase audit in your first 30 days without making everyone hate you. If you have a horror story from a codebase you inherited, I want to hear it. The worst ones are always the most educational.

A developer staring at a messy codebase on multiple monitors, looking overwhelmed

Two engineers discussing code on a whiteboard, one pointing at a diagram

A laptop screen showing a terminal with git log output and a coffee cup nearby

The Hardest Problem in Computer Science Is Still Naming Things — And Your Postmortem Titles Prove It

The Hardest Problem in Computer Science Is Still Naming Things — And Your Postmortem Titles Prove It

Every engineering team has a values poster. Every engineering team also has a service called “data-service-v2.” One of these tells you the truth about the culture.

You know the joke. Two hard problems in computer science: cache invalidation, naming things, and off-by-one errors. It’s funny because it’s three things, and because every engineer has lived it. But the joke trained us to think of naming as a technical annoyance — a friction point between writing code and shipping it, something you solve with a thesaurus and a team vote, then forget about.

I want to argue something different. The names your team chooses — for services, projects, architecture decisions, incident postmortems, refactoring initiatives, Slack channels — are the most honest cultural artifacts you produce. More honest than your values poster. More honest than your engineering handbook. More honest than that paragraph on your careers page about “engineering excellence.” Names encode decisions. They encode who was in the room when the decision was made. They encode what the team knew at the time, what it was afraid of, and what it was trying to avoid admitting. And bad names persist not because teams are lazy — they persist because renaming is politically expensive, because the person who chose the name has usually left, and because the name often encodes a decision that someone else made in a meeting you weren’t invited to.

If you want to diagnose a team’s culture, don’t read its style guide. Read its names.

§ 1 — The Service Called “Platform”

I once joined a team that had a service called “platform.” I asked what it did. Three engineers gave me three different answers. One said it was an API gateway. One said it was a data aggregation layer. One said it was “the thing that talks to the vendors.” The service was four years old, had twelve contributors in its git history — seven of whom had left the company — and had accumulated so many responsibilities that any attempt to describe it required the word “also” at least twice per sentence.

The name “platform” wasn’t a mistake. It was a fossil. At the time it was named, it probably was a platform — a thin layer abstracting away vendor integrations. But the name carried no constraints, and because it carried no constraints, it accumulated every responsibility no other service wanted to own. “Platform” is a name that means “whatever we need it to mean,” and that kind of name is a standing invitation to dump work into a service nobody has to think about until it breaks.

Generic names aren’t a naming problem. They’re a boundary problem. A service called “platform” or “core” or “common” or “utils” is a service with no edges, and a service with no edges grows until it becomes the place where engineering goes to die. The name doesn’t cause the dysfunction. It enables it. The name says: this thing has no specific purpose, so any purpose will do.

I’ve seen this pattern at every company I’ve worked at. The service called “gateway” that became a monolith. The service called “shared” that every team depended on and no team owned. The service called “legacy” that was newer than three services called “new-thing.” The names are diagnostic — they tell you that at some point, the team stopped being willing to draw boundaries, and the name was the first place that reluctance showed up.

§ 2 — Version Numbers That Erase History

Then there’s the version-number name. “data-service-v2.” “auth-rewrite.” “payments-v3.” These names are everywhere, and they share the same pathology: they tell you something came before, but they don’t tell you what changed, why it changed, or what the previous version got wrong.

“data-service-v2” answers the question “is this the new one?” and refuses to answer any other question. What was v1? Why did it need replacing? Is v2 a rewrite, a refactor, or a parallel implementation? Will v1 be decommissioned, or will it run alongside v2 for the next three years while everyone pretends that’s temporary? The name gives you nothing.

The version-number name is a symptom of a team that decided not to commit to a narrative. Naming something “v2” is a way of saying “we’re moving forward” without specifying what forward means. It’s the engineering equivalent of “new and improved” — technically informative, culturally useless. And these names persist because the moment you give something a specific name — “the event-sourced billing service” or “the synchronous payments gateway” — you’ve made a commitment about what it is and what it isn’t. Version numbers let you dodge that commitment.

I’ve watched teams build v3 of a service while v2 was still being built. I’ve watched teams maintain v1, v2, and v3 simultaneously, each with its own on-call rotation, because nobody could agree on which version was canonical. The version-number name doesn’t track progress. It tracks the accumulation of decisions nobody was willing to close out.

§ 3 — Incident Names That Minimize What Happened

This is where naming gets serious. If your service names are cultural artifacts, your incident names are cultural fingerprints.

I’ve seen postmortems titled “Minor API degradation on March 14” that described a four-hour outage affecting the entire checkout flow. I’ve seen incidents codenamed “wobbly Tuesday” that caused six-figure revenue loss. I’ve seen an incident called “the blip” that took down authentication for forty minutes and locked out every internal tool — including the incident management dashboard.

The names we give incidents aren’t neutral labels. They’re organizational signals about how seriously we’re taking what happened. A postmortem called “minor API degradation” tells every engineer who reads it — and every executive who skims it — that this wasn’t a big deal. The four-hour checkout outage is already being softened before the root cause analysis is written. The name is the first draft of the spin.

Google’s SRE book dedicates an entire chapter to postmortem culture, and its table of contents reads like a taxonomy of operational seriousness — “Managing Incidents,” “Postmortem Culture: Learning from Failure,” “Tracking Outages” — each one a named, structured practice with formal expectations. The Google SRE book’s treatment of incident documentation makes clear that postmortems are first-class engineering artifacts, not afterthoughts dashed off between pages. Mature engineering organizations treat the naming and documentation of incidents as a deliberate practice. Immature ones treat it as a creative writing exercise where the goal is making the incident sound smaller than it was.

The minimizing incident name is a trust problem wearing a naming problem’s clothes. Teams name incidents carefully when they’re afraid of consequences. They name incidents casually when they don’t want anyone outside the team to pay attention. They name incidents with cutesy codenames when they want to signal that this is an internal matter, not something the business needs to worry about. Every one of these naming choices tells you what the team is afraid of.

§ 4 — The ADR That Records the Decision But Not the Duress

Architecture Decision Records are supposed to be the antidote to tribal knowledge. They’re supposed to capture not just what was decided but why — what alternatives were considered, what tradeoffs were accepted, what constraints were in play. In practice, most ADRs I’ve read are post-hoc justifications written by the person who won the argument, and their titles reflect it.

“ADR-014: Adopt Kafka for Event Streaming” tells you what was decided. It does not tell you that the team owning the message queue was overruled, that the Kafka migration was driven by a VP who saw a conference talk, or that the team’s preferred option — RabbitMQ, already running in production — was dismissed without a technical evaluation. The ADR title is a clean summary of a dirty process.

The problem isn’t that ADRs are dishonest. The problem is that the naming convention — “ADR-NN: Decision” — encodes only the outcome. The title becomes the canonical reference, and once it’s referenced in three other documents and two Slack threads, it’s locked in. Nobody goes back and amends an ADR title to say “ADR-014: Adopt Kafka Because the VP Saw a Conference Talk and the Team Was Outvoted.” That would be honest. It would also be career-limiting.

So the ADR title becomes a small fiction. Not a lie, but a simplification that erases the political context in which the decision was made. And the next engineer who reads it thinks the decision was made for the reasons stated, because the title gives them no reason to suspect otherwise.

§ 5 — Who Gets to Name Things

Here’s the pattern I’ve seen at every company: the person who creates the thing gets to name it. The person who creates the thing is usually the person with the most context at the time of creation. The person with the most context is usually the person who’s been there longest, or the person with the most political capital, or the person who happened to be in the room when the Jira ticket was created.

This means naming isn’t a democratic process. It’s an exercise in power. The senior engineer who spins up a new service at 11 PM names it. The junior engineer who joins six months later has to live with that name, work with it, explain it to new hires, and pretend it makes sense.

I worked with a team once where every service was named after a Greek myth. Atlas. Prometheus. Icarus. Sisyphus. The engineer who named them was brilliant, had a humanities degree, and left after eighteen months. The remaining team spent the next two years explaining to new hires why the billing service was called Sisyphus. The answer, as far as anyone could reconstruct, was that the engineer thought billing was a repetitive, futile task. This was either a joke or a cry for help, and by the time I got there, nobody could remember which.

Names encode the values and context of the person who chose them. When that person leaves, the name becomes a question nobody can answer. This is why renaming is so hard — not because it’s technically difficult (it’s a find-and-replace), but because renaming is an act of saying “the person who named this was wrong, and I know better.” That’s a political statement, not a technical one. Most engineers don’t want to make it.

§ 6 — The Political Cost of Renaming

So bad names persist. They persist because the cost of renaming is paid by the person who proposes it, and the benefit is distributed across everyone who will ever interact with the name in the future. The proposer takes the political risk of offending the original namer (if they’re still around), the technical risk of updating every reference, and the social risk of being seen as someone who cares about “trivial” things like names instead of “real” things like features.

This is the same dynamic that keeps bad code alive. The cost of refactoring is concentrated; the benefit is distributed. The difference is that with code, we at least have language for talking about technical debt. We have sprints for it. We have metrics that make the cost of inaction visible. With naming, we don’t. There’s no Jira label for “this name is actively misleading every new hire.” There’s no dashboard showing the hours lost to confusion about what “platform” does.

The result is that names ossify. They become part of the team’s vocabulary without ever being validated. Engineers learn them the way you learn the quirks of a codebase — not because they make sense, but because they’re there, and questioning them takes more energy than accepting them. The name becomes load-bearing. You can’t remove it without shaking the structure, and nobody wants to be the person who shakes the structure.

The NIST Cybersecurity Framework demonstrates what happens when an organization treats naming and categorization as a first-class discipline. Its structured taxonomy of risk outcomes — Govern, Identify, Protect, Detect, Respond, Recover — represents deliberate, institutional investment in naming as a foundation for shared understanding. The contrast with engineering teams that name incidents “the blip” and services “platform” is not subtle. When a government standards body takes classification more seriously than your engineering organization does, your engineering organization has a naming problem — and a culture problem.

§ 7 — Practical Experiments: Auditing Your Team’s Names

So what do you do about it? You can’t rename everything. You shouldn’t try. But you can start treating naming as a first-class engineering decision rather than an afterthought dashed off between standup and lunch. Here are three experiments I’ve run on teams I’ve been part of, with varying degrees of success and one outright failure that taught me more than the successes.

Experiment 1: The New-Hire Naming Audit. Ask someone who joined in the last 90 days to list every service, project, and recurring meeting name they’ve encountered, and write down what they think each one means. Don’t correct them. Just collect the answers. The gap between what the name says and what the new hire thinks it means is your naming debt, made visible. I ran this once and discovered that three engineers thought “the bridge” was a data pipeline, a Slack channel, and a deployment process. It was all three. The name “bridge” had been reused for different things by different teams over three years, and nobody noticed because nobody new had ever been asked.

Experiment 2: The Postmortem Title Review. Before your next postmortem is published, have someone who wasn’t involved in the incident read the title and guess the severity. If they guess low, the title is doing work for you — specifically, the work of minimizing what happened. Rewrite the title until a stranger can accurately gauge the impact. This sounds trivial. It is not. The first time I tried this, the original title was “intermittent 5xx errors in the EU region.” The rewritten title was “Checkout unavailable for 47% of EU customers for 2.5 hours due to connection pool exhaustion.” The first title was written by the on-call engineer. The second was written by the engineer who had to explain the incident to the CFO. Both were accurate. Only one was honest.

Experiment 3: The Rename One Thing Challenge. Pick one service, one document, or one recurring meeting with a bad name. Rename it. Track every reference. Update every link. Write a one-paragraph note explaining the old name, the new name, and why the change was made. This is deliberately small. The point isn’t to fix all your names — it’s to prove that renaming is survivable, to establish that names can change, and to create a template for the next person who wants to try. The political cost of the first rename is the highest. Every subsequent rename is cheaper because the precedent exists.

If you want to go deeper, borrow naming discipline from adjacent fields. Editorial teams use structured naming conventions for manuscripts and campaigns — a book title generator that enforces structure can teach you something about why constraints produce better names than open-ended brainstorming sessions. The principle is the same: names that follow a pattern carry information. Names chosen freely carry whatever the namer was thinking about that day, which is usually lunch.

§ 8 — What Your Names Are Telling You

Here’s what I want you to take away. Your team’s names are not a cosmetic concern. They’re a diagnostic surface. They tell you where your team has stopped drawing boundaries (generic names), where it has stopped committing to narratives (version numbers), where it is minimizing failure (cutesy incident names), where it is hiding political decisions behind technical summaries (ADR titles), and where it has let the context of departed engineers calcify into permanent confusion (orphaned codenames).

Every name is a small decision, but the aggregate pattern tells you something your retros never will — because retros are performed for an audience, and names are chosen when nobody’s watching. Names are what your team does when it thinks nobody is keeping score.

So go look at your service catalog. Read your last ten postmortem titles. Open your ADR repository and read the titles without reading the bodies. Ask yourself: do these names tell me what I need to know? Do they carry context, or do they require it? And if they require context — if every name comes with a story that isn’t in the name — then your team has a documentation problem no style guide will fix. Because the problem was never the names. The problem was the meetings where the names were chosen, who was in those meetings, who wasn’t, and the fact that nobody has been willing to revisit those decisions since.

Naming is the hardest problem in computer science because it’s the one that requires the most honesty. You can solve cache invalidation with better infrastructure. You can fix off-by-one errors with tests. But you can only fix naming by being willing to say: this name is wrong, this name is misleading, this name was chosen by someone who is gone, and I’m going to change it. That’s not a technical decision. It’s an act of cultural maintenance. And it’s the act that distinguishes a team that manages its knowledge from a team that just accumulates it.

Why Your Standup Is a Stand-Down: The Case for Async Engineering

A developer staring blankly at a wall of sticky notes, representing meeting overload

Let’s call your daily standup what it really is: a hostage situation. You’re standing in a circle—or staring at a grid of grainy faces—while Dave from DevOps recites his life story disguised as a status update. You’re not collaborating. You’re waiting for your turn to speak so you can finally get back to the work that the meeting itself is preventing you from doing. The biggest technical problem in most engineering orgs isn’t a microservice latency spike or a memory leak in production. It’s the synchronous communication tax that bleeds your team dry, one 30-minute block at a time.

I’m Fritz Muller. I’ve spent enough years in the trenches to know that the best code gets written when nobody is talking to you. The cult of the meeting has convinced managers that butts in seats—or cameras on—equals productivity. It’s a comforting lie. What it actually equals is context-switching, burnout, and a codebase that looks like it was assembled by a committee of caffeinated squirrels. We need to treat engineering culture with the same rigor we apply to our systems. The diagnosis is clear: fewer meetings, more async.

The Synchronous Sinkhole

Synchronous communication—meetings, impromptu desk taps, Slack huddles—is a brute-force solution to a coordination problem. It assumes the fastest way to align is to stop everyone’s world at the same time. For a production firefight, sure. Grab the war room. But for a daily ritual that asks, “What did you do yesterday?” you’re torching cognitive fuel for a status report that could be a three-line message. The math is ugly. A 15-minute standup for a team of six engineers doesn’t cost 15 minutes. It costs 90 minutes of focused work, plus the 20-minute ramp-up each person needs to get back into flow. That’s a half-day of productivity, vaporized, every single week. For a status update.

And it’s not just standups. Sprint planning, retrospectives, “quick syncs” that metastasize into architecture debates—these are all symptoms of a deeper dysfunction. We default to meetings because we don’t trust the written word. We don’t trust that a design doc will be read, that a comment in Linear will be seen, that a Loom video will be watched. So we force the interaction, guaranteeing that the work stops. It’s the organizational equivalent of a distributed system that uses blocking I/O instead of an event loop. It doesn’t scale.

Async Is a System Design Choice

Asynchronous communication isn’t just “sending an email instead of having a meeting.” It’s a fundamental re-architecture of how your team processes information. Think of it as moving from a tightly coupled monolith to a well-designed event-driven architecture. Each message—a pull request description, a technical spec, a recorded demo—is an event. Team members consume those events when their own event loop is ready, process them, and emit their own events in response. No blocking. No thread starvation. Just a steady, high-throughput stream of progress.

This requires discipline. You can’t just fire off a half-baked Slack message and call it async. The quality of the artifact matters. A good async update is self-contained, provides context, and anticipates questions. It’s the difference between a commit message that says “fix bug” and one that explains the root cause, the fix, and the testing done. The former creates more meetings. The latter closes the loop. When you treat communication as a first-class engineering artifact, you start to see meetings as the exception handler, not the main execution path.

A person typing on a laptop with a notepad and coffee, representing focused asynchronous work

The Tools Are Already There, You’re Just Using Them Wrong

Your team already has the tools for a fully async workflow. You’re just using them as a notification layer for the next meeting. Let’s run through the stack:

Slack (or Teams, or Whatever Microsoft Is Calling It This Week)

Stop using it for real-time chatter. Use it for persistent, searchable, threaded updates. A channel per project, with a strict norm: no “@here” unless the server room is literally on fire. Daily standup updates go in a dedicated thread. People read them when they start their day. Questions and clarifications happen in the thread, asynchronously. If a thread spirals into a debate that needs higher bandwidth, then you schedule a focused, time-boxed call with a clear agenda and only the necessary people. The default is text. The exception is voice.

Pull Requests as the Primary Collaboration Point

A pull request is not just a code review. It’s a design discussion, a knowledge transfer, and a historical record. Write PR descriptions that explain the “why,” not just the “what.” Link to the spec, the ticket, the previous failed attempt. Reviewers should leave comments that are complete thoughts, not “let’s hop on a call to discuss.” If you find yourself typing “let’s sync,” stop. Type out your concern. Propose an alternative. Attach a screenshot. Treat the PR as the canonical source of truth for that change. A well-run PR process eliminates the need for a separate “code review meeting” and a “design review meeting” and a “let’s make sure we’re all on the same page” meeting.

Recorded Demos and Loom Videos

Live demos are a waste of collective time. They’re a performance where one person fumbles with their terminal while 10 others pretend to care. Record a five-minute walkthrough. Show the happy path, the edge cases, and where the bodies are buried. Post it in the project channel. People watch it on 2x speed when they’re in the right headspace. They pause, rewind, and leave timestamped comments. You’ve just turned a synchronous 30-minute meeting into an asynchronous, self-serve resource. If you’re not doing this, you’re choosing ceremony over effectiveness.

Decision Records and RFCs

For any non-trivial technical decision, write a short document. Outline the problem, the options considered, the trade-offs, and the chosen path. Circulate it for comment. Give people 24 hours to read and respond. Then, if there’s still disagreement, have a focused discussion. This is the “Request for Comments” process that built the internet. It works. It prevents the “let’s get everyone in a room and argue” anti-pattern that leads to design-by-committee disasters and loudest-voice-wins architecture.

The Cultural Shift: From Presence to Progress

Moving to async isn’t a tooling problem. It’s a trust problem. Managers who demand synchronous standups are often signaling that they don’t trust their engineers to be working unless they’re seen working. This is factory-floor thinking applied to knowledge work. It’s stupid. You hired smart people. Let them prove their progress through artifacts, not attendance. The measure of an engineer is the code they ship, the bugs they fix, the designs they document—not the eloquence of their morning monologue.

This shift requires explicit norms. Write them down. “We default to asynchronous communication. Meetings are a last resort, scheduled with a clear agenda and a hard stop. Status updates are written, not spoken. Decisions are documented, not shouted.” Then, enforce it. When someone tries to schedule a meeting to discuss something that could be a document, push back. Ask for the doc. When someone interrupts a colleague with a tap on the shoulder, remind them of the async-first norm. This is culture change, and culture change is hard. But so is debugging a race condition at 2 a.m. You do it because the alternative is worse.

One of the biggest objections I hear is, “But we’ll lose the human connection!” This is sentimental nonsense. You don’t build camaraderie by forcing everyone to recite their Jira tickets in a circle. You build it by working together on hard problems, by having thoughtful async debates, and by occasionally getting together for a non-mandatory, actually-fun social event. The forced fun of a daily standup is about as bonding as a root canal. Real connection happens when you respect people’s time and autonomy, not when you treat them like children who need a morning roll call.

A team collaborating casually around a table, representing healthy, non-forced interaction

Handling the Hard Cases

Some work genuinely benefits from synchronous discussion. Brainstorming new features, resolving a complex architectural dispute, or giving tough feedback. The key is to be intentional. Schedule a focused session with a clear outcome. Use a technique like “silent reading” of a proposal doc for the first 10 minutes, so everyone is on the same page before a single word is spoken. Time-box the discussion ruthlessly. If it’s not resolved, document the open questions and take it async again. The meeting is a tool, not a lifestyle.

Another hard case: the engineer who refuses to write. Some people are just bad at written communication. They’d rather talk for an hour than write a coherent paragraph. This is a performance issue, not a communication preference. In a modern engineering team, the ability to write clearly is as fundamental as the ability to write clean code. If an engineer can’t document their design, they can’t scale their impact. Coach them. Give them templates. But don’t let their weakness drag the whole team back into the meeting room. The team’s throughput is more important than one person’s comfort zone.

Measuring What Matters

If you stop measuring hours-in-meetings and start measuring artifacts-produced, you’ll see a shift. Track the number of decisions documented per week. Track the median time-to-merge for pull requests. Track the number of unplanned interruptions. These are the metrics of a healthy engineering team. When you cut the meetings, you’ll see a spike in deep work. You’ll see fewer bugs because people have time to think. You’ll see better design because proposals are written, circulated, and critiqued thoughtfully instead of being whiteboarded in a rush 10 minutes before the next standup.

And for the love of all that is compilable, stop with the “daily standup is sacred” religion. It’s not. It’s a process smell from a framework that was designed for co-located teams building monoliths in the early 2000s. We’re building distributed systems with distributed teams. Our communication patterns should match our architecture. Async is not a compromise. It’s an upgrade.

Frequently Asked Questions

Won’t async communication slow down decision-making?

Only if you confuse activity with progress. A decision made in a 30-minute meeting that wasn’t thought through will be unmade in the following days, costing far more time. Async decision-making, with a clear proposal and a 24-hour comment period, often leads to faster implementation because the decision is more durable. For true emergencies, you still have the phone. But how many of your daily decisions are actual emergencies?

How do we keep remote team members from feeling isolated without daily calls?

Isolation doesn’t come from a lack of meetings. It comes from a lack of meaningful interaction and shared context. Async practices like well-written project updates, active discussion threads on technical decisions, and occasional, well-planned virtual social events build stronger connections than a daily status call where 80% of the time is spent on updates irrelevant to most attendees. Focus on the quality of interaction, not the frequency.

What if management insists on synchronous standups?

This is a political problem, not a technical one. Gather data. Run a two-week experiment where the team does async standups in a dedicated Slack thread. Measure the team’s output, the number of blockers resolved, and the team’s self-reported satisfaction. Present the results. If management still insists on the meeting after seeing that the team is more productive and happier without it, you have a culture problem that no amount of process tweaking will fix. At that point, you might consider whether you want to work for people who value ritual over results.

Doesn’t async communication just replace meetings with endless Slack threads?

It can, if you do it poorly. The goal isn’t to move the meeting into a chat window. It’s to change the nature of the communication. A good async update is a structured, thoughtful artifact. It’s not a stream of consciousness. It has a clear subject, context, and call to action. It’s designed to be consumed efficiently. If your Slack threads are turning into endless, unstructured back-and-forths, the problem isn’t async—it’s that you’re still communicating as if you’re in a meeting. Apply the same rigor to your writing that you apply to your code.

The bottom line: your team’s most precious resource is uninterrupted thinking time. Protect it like you protect your production database. Default to async. Write things down. Trust your people to read and respond. The result will be a quieter, calmer, and dramatically more productive engineering culture. And you’ll finally get that standup time back to do what you actually love: building things.

Why Your Engineering Team Needs Fewer Meetings and More Async Communication

I’ve sat through enough stand-ups that somehow became 45-minute architecture debates to know we’ve got a problem. The modern engineering team doesn’t lack communication. It’s drowning in it—specifically, the kind that demands everyone stop working at the exact same moment. If your calendar looks like a losing game of Tetris and actual coding only happens in the cracks between meetings, this one’s for you.

The Meeting Industrial Complex

Somewhere along the line, we bought into the idea that more meetings mean more alignment. That’s garbage. What they really produce is context-switching, the silent productivity killer. Yank a developer out of their flow to discuss something that could’ve been a written update, and you’ve torched at least 30 minutes of real work—the meeting itself, plus the mental rebuild to get back into the problem. Multiply that by a team of six, and your “quick sync” just cost the company half a day of output. Not exactly a bargain.

I’m not some meeting abolitionist. Some conversations genuinely need real-time back-and-forth. But the default setting should be asynchronous communication: written, recorded, or otherwise time-shifted so people can engage when their brain is actually ready. The meeting should be the exception, a break-glass-in-case-of-emergency option, not the rhythm of your week.

Engineer staring at a cluttered calendar on a laptop screen

Why Meetings Are a Technical Problem

Engineers obsess over optimizing systems. We’ll argue for hours about the right database index, the perfect API contract, or whether tabs are morally superior to spaces. But we almost never apply that same rigor to the system of how we work together. A meeting isn’t just a meeting—it’s a synchronization point in a distributed system (your team), and every sync point introduces latency. If you wouldn’t design a microservice architecture where every service has to phone home to a central monolith before doing anything, why would you design your team that way?

Async communication is the event-driven architecture of teamwork. You emit a message—a design doc, a Loom video, a well-structured Slack thread—and your teammates consume it when they’re ready. No blocking. No forced context switches. Just pure, beautiful throughput. The kind that actually ships features.

The Stand-Up That Wouldn’t Die

Daily stand-ups are the worst offender. What began as a quick huddle to unblock work has mutated into a status-reporting ritual that serves managers more than makers. If your stand-up involves people reciting what they did yesterday while everyone else mentally checks out, you’ve got a meeting that should be a Slack bot. Automate the status updates. Reserve synchronous time for actual collaboration—the messy, creative work that needs voices in a room.

Decision-Making by Default

Another trap: calling a meeting every time a decision needs to be made. This breeds a culture where nobody feels empowered to decide anything without a quorum. Instead, push for written proposals. A well-structured RFC (Request for Comments) document lets people weigh in on their own time, asynchronously, and creates a paper trail that’s infinitely more useful than half-remembered meeting notes. If the RFC doesn’t resolve the issue, then you schedule a focused discussion—but that’s the backup plan, not the starting point.

Two engineers collaborating over a whiteboard with sticky notes

The Async Toolkit: What Actually Works

Going async isn’t just about killing meetings. It’s about replacing them with better systems. Here’s what I’ve seen work on teams that actually ship software instead of just talking about it:

  • Written design docs over whiteboard sessions. A good design doc forces the author to think through edge cases and trade-offs before anyone else spends brain cycles on it. It also creates a permanent artifact that new team members can reference six months later when they’re wondering why the hell the system works that way.
  • Loom videos for code walkthroughs. Record it once. Watch it at 2x speed. Pause when you need to. This is strictly better than a live screen share where half the attendees are multitasking anyway.
  • Decision logs in a shared space. Not meeting notes—decision logs. A single place where you record what was decided, by whom, and why. No more “I thought we agreed on the other approach” arguments three weeks later.
  • Async stand-ups via Slack or a bot. Post your update in a thread. Read others’ updates when it fits your day. If something’s blocking you, flag it and someone can jump in. No need to warm 10 chairs for 15 minutes.

The Cultural Shift: Writing Over Talking

Here’s the uncomfortable part: async communication demands writing. Clear, structured, thoughtful writing. And a lot of engineers—and especially managers—are lazy writers. They’d rather hop on a call and ramble for 20 minutes than spend 10 minutes crafting a coherent document. But that call just externalized the cost of their laziness onto everyone else’s schedule.

Writing is thinking. When you force yourself to write down a proposal, you discover the gaps in your own logic. When you write a status update, you have to actually reflect on what you accomplished. When you write a meeting summary, you create a record that outlasts the meeting. This is a skill, and teams that invest in it become dramatically more effective. The ones that don’t just keep having meetings about why nothing gets done.

But What About “Human Connection”?

I hear this one a lot: “Meetings build team culture!” No, they don’t. Forced proximity builds resentment. What builds culture is trust, autonomy, and the occasional well-timed meme in a Slack channel. If your team’s only social interaction is status meetings, you’ve got a culture problem that more meetings won’t fix. Schedule a virtual coffee or a team lunch. Don’t pretend your sprint planning is a bonding experience.

Engineer working alone in a quiet home office with plants

How to Start Cutting Meetings Without Getting Fired

If you’re not the one setting the meeting culture, you can still nudge it. Start small. Pick one recurring meeting that everyone agrees is useless—there’s always at least one—and propose an async replacement for a trial period. Show results. Did the same information get shared? Did decisions still get made? Did people get more done? When the answer is yes (and it will be), you’ve got ammunition for the next one.

If you are in a position to set policy, do it explicitly. Declare a “meeting-free Wednesday” or cap recurring meetings at 30 minutes. Better yet, require a written agenda and expected outcome for any meeting invite. If the organizer can’t articulate what decision will be made or what problem will be solved, the meeting doesn’t happen. You’ll be amazed how many meetings simply evaporate.

FAQ: The Pushback You’ll Get

“But some things just need a real-time conversation!”

Agreed. Nobody’s saying abolish all meetings. The point is to make real-time the last resort, not the first. When a Slack thread hits 50 messages and people are talking past each other, sure, hop on a quick call. But that’s a symptom that the async approach wasn’t structured well, not proof that async doesn’t work.

“Won’t this slow down decision-making?”

Only if you confuse activity with progress. A meeting gives the illusion of speed because everyone’s in the room, but how many decisions actually get made? And how many get unmade the next day because someone wasn’t in the room? Async decision-making with written proposals is often faster because it eliminates scheduling overhead and lets people think before they commit.

“What about junior engineers who need mentorship?”

Mentorship doesn’t require a meeting. Pair programming sessions, code reviews, and async Q&A in a public channel are all mentorship. In fact, written feedback is often better for juniors because they can refer back to it. The key is to be responsive, not to be in the same room at the same time.

“Our team is remote across time zones—meetings are the only time we’re all together!”

That’s exactly why you should have fewer meetings. Forcing someone to attend a 9 PM stand-up is a quick way to burn them out. Async communication is the great equalizer for distributed teams. Record updates, use threaded discussions, and let people contribute when they’re at their best. The “togetherness” argument is usually just discomfort with giving up control.

The Bottom Line

Engineering is a creative, cognitive discipline. Every interruption is a tax on output. Meetings are the most expensive tax there is because they’re scheduled, recurring, and socially mandatory. The best teams I’ve seen treat meeting invites like pull requests: they require justification, they’re scoped tightly, and they get rejected if they don’t add value.

So look at your calendar for next week. Count the hours blocked off for synchronous communication. Then ask yourself: how many of those could have been a well-written document, a short video, or a threaded Slack discussion? If the answer is more than zero—and it will be—you’ve got work to do. Your team’s productivity, and probably their sanity, depends on it.