Skip to main content

Shipping less can be a systems decision, not just product restraint: A Practical View

I usually care more about what shipping less does to my system's shape than to my product backlog. When I pushed back on adding another auth provider last sprint, it wasn't product caution—it was a systems call about secret sprawl and deployment complexity. The tension is real: shipping less looks like product failure from one side and systems prudence from the other.

This isn't about saying no. It's about choosing where to spend engineering capital—on features or on keeping the system legible under pressure. The difference shows up at 2 AM when you're debugging a failing webhook from a service you forgot you integrated six months ago.

The Real Problem

Recent attacks on open source have focused on exfiltrating secrets. GitHub's post on securing the supply chain across their platform shows how each new integration point becomes a potential leak. Every auth provider you add isn't just another button on a login screen. It's another set of secrets to rotate, another webhook endpoint to secure, another dependency to update when something breaks, and another incident response runbook you'll need to write at 3 AM.

I read the GitHub architecture optimization post about making diff lines performant. What surprised me wasn't the technical solution—it was the admission that the path to better performance was found in simplicity. They didn't add more caching layers; they questioned whether every feature needed to exist in the first place. That's the insight I keep coming back to: performance and security often share the same root cause—too many moving parts.

The practical question for me is always: what does this feature cost when it breaks at 2 AM? Not the development cost. The operational cost. How many new log patterns do I need to grep for? How many new metrics do I need to add to my dashboards? How many new Slack channels will light up when this thing fails?

Where Teams Usually Get It Wrong

Most teams treat scope reduction as a product negotiation. Product says "we need feature X," engineering says "that's a lot of work," and they meet in the middle with a stripped-down version that still carries most of the complexity. That's not shipping less. That's shipping brittle.

The mistake is framing it as what you're not building. That puts everyone on defense. Product feels like they're losing. Engineering feels guilty for pushing back. The conversation becomes about ambition rather than architecture. I've sat in meetings where we argued for an hour about whether to build a "lite" version of a feature, only to realize later that the lite version still required three new microservices and a message queue.

I would rather have a conversation about deployment targets. Every new auth provider means another deployment target to configure, another set of environment variables to manage, another place where a misconfiguration could expose user data. That's not product strategy. That's systems risk. And it compounds. Each new target needs its own CI/CD pipeline, its own canary deployment strategy, its own rollback procedure.

The part I would watch is how this shows up in your incident response. When you have five auth providers and one starts failing, do you even know which customers are affected? Or do you page through logs trying to figure out if it's the Google OAuth client or the Microsoft one or the GitHub one? I've been in that situation. It's not fun.

A Better Working Shape

A few months ago, I started counting secrets. Not metaphorically—actually counting how many new secrets each proposed feature would require. Auth providers need client IDs and secrets. Payment processors need API keys. Third-party webhooks need signing secrets. AI services need endpoint URLs and API keys. Each one is a potential entry point.

Suddenly, the conversation changed. "This feature needs four new secrets" carried more weight than "this will take two sprints." It made the cost tangible. Secret sprawl is real, and GitHub's supply chain security work shows why it matters. Each secret is a potential entry point for attackers, and each one needs rotation policies, access controls, and audit logging.

The useful part was that it shifted the discussion from product value to systems impact. We weren't asking "is this feature worth building?" We were asking "is this feature worth the operational burden?" Those are different questions, and they lead to better decisions. The Azure AI Foundry docs make this concrete—they show how each new model endpoint you add increases not just cost, but latency, monitoring overhead, and failure modes.

I started keeping a simple spreadsheet. Each row was a proposed integration. Columns for: secrets required, deployment targets, new error types, monitoring gaps, and on-call runbook length. It wasn't scientific. But it was honest. When product saw that the "simple" social login feature would add seven new secrets and three new deployment targets, they started asking different questions. They started thinking about systems impact too.

What to Watch in Practice

Start counting what matters. When someone proposes a new integration, ask:

  • How many new secrets does this require?
  • How many new deployment targets?
  • How many new error paths to monitor?
  • What happens when this service is down?
  • How do we roll back if it breaks?
  • What new alerts will we need to create?
  • Which runbooks need updating?

These aren't product questions. They're systems questions. And they're questions engineering should own. I've found that having these questions written down in a shared doc helps. It becomes a checklist, not a negotiation.

The Copilot CLI /fleet feature is a good example of this thinking in action. It lets you run multiple agents in parallel, but the post about it spends most of its time talking about how to avoid common pitfalls—dependency management, error handling, avoiding race conditions. That's the right focus. The value isn't in adding more agents; it's in managing the complexity when you do.

I keep coming back to the GitHub diff performance post. They didn't optimize their way out of the problem. They simplified their way out. That's the pattern I look for. Can we solve this with less code, fewer dependencies, and fewer moving parts? If yes, that's not restraint—that's engineering judgment.

The part I would watch is how you frame the conversation. "Scope reduction" in Jira feels like defeat. "Attack surface minimization" in a security review feels like wisdom. The humor is in the rebranding, not in jokes. But the reality is that simpler systems ship faster, debug easier, and fail less often. And when they do fail, you can actually understand what happened.

How This Changes Day-to-Day Work

This approach changes three things about how you work.

First, your pull requests get smaller. Not because you're writing less code, but because you're deleting more. I've had PRs that were 80% deletions—removing an old auth provider, cleaning up unused endpoints, deleting deprecated config. Those are some of the most valuable PRs I've shipped.

Second, your on-call gets quieter. Fewer services mean fewer pages. Fewer integrations mean fewer third-party outages that cascade into your system. I used to get paged because a social media API changed their rate limits. Now I don't have that integration. My on-call is quieter. That's a feature.

Third, your deployment process gets simpler. I remember a project where we had five different deployment targets for five different auth providers. Each one needed its own environment variables, its own smoke tests, its own rollback strategy. When we consolidated to two providers, our deployment time dropped by half. Our rollback time dropped to zero because we could just flip a feature flag.

The fear is being seen as blocking progress. But the advantage is that boring deployments are the goal. No one celebrates a deployment that works. They only complain when it doesn't. Shipping less is how you make deployments boring.

Closing Heuristics

When you're evaluating whether to ship something, measure it by what it adds to your system, not just what it adds to your product. Count the secrets. Count the deployment targets. Count the new error messages you'll need to document.

If you can't justify the systems cost, don't build it. That's not product failure. That's systems thinking.

Here are the rules I use:

  1. One in, one out. Adding a new integration? Remove an old one. Keep the total number of secrets flat.
  2. No new deployment targets without a fight. Each one needs to justify its existence in terms of uptime, not features.
  3. If it needs a new runbook, it's too complex. Your runbooks should fit on one screen.
  4. Count the alerts. More than five new alerts for a feature? That's a monitoring tax you can't afford.
  5. Ask what breaks at 2 AM. If you can't answer that question in 30 seconds, the feature is too complex.

And the next time someone calls you out for shipping less, don't apologize. Explain what you kept out. That's a better story anyway.

Resources Worth Reading

Related Reading