惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Apple Machine Learning Research
Apple Machine Learning Research
AWS News Blog
AWS News Blog
Google DeepMind News
Google DeepMind News
U
Unit 42
博客园 - 叶小钗
博客园 - 聂微东
GbyAI
GbyAI
Stack Overflow Blog
Stack Overflow Blog
有赞技术团队
有赞技术团队
aimingoo的专栏
aimingoo的专栏
D
DataBreaches.Net
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
Jina AI
Jina AI
美团技术团队
The Cloudflare Blog
M
MIT News - Artificial intelligence
Microsoft Azure Blog
Microsoft Azure Blog
I
InfoQ
S
Schneier on Security
C
Check Point Blog
Project Zero
Project Zero
The Hacker News
The Hacker News
Scott Helme
Scott Helme
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
Cisco Talos Blog
Cisco Talos Blog
P
Privacy International News Feed
SecWiki News
SecWiki News
Latest news
Latest news
MongoDB | Blog
MongoDB | Blog
S
Secure Thoughts
Google Online Security Blog
Google Online Security Blog
F
Fortinet All Blogs
博客园 - 三生石上(FineUI控件)
H
Help Net Security
TaoSecurity Blog
TaoSecurity Blog
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
Last Week in AI
Last Week in AI
P
Privacy & Cybersecurity Law Blog
Forbes - Security
Forbes - Security
G
GRAHAM CLULEY
N
Netflix TechBlog - Medium
L
Lohrmann on Cybersecurity
A
About on SuperTechFans
T
The Exploit Database - CXSecurity.com
C
Cisco Blogs
PCI Perspectives
PCI Perspectives
大猫的无限游戏
大猫的无限游戏
T
Troy Hunt's Blog
H
Hacker News: Front Page
Vercel News
Vercel News

Yusuf Aytas

When Code Is Cheap, Does Quality Still Matter? Why Crouching Tiger, Hidden Dragon Is a Masterpiece Why We Ignore Advice The Mirror Is Part of the Machine When Too Many Maps Overlap on One Person The Work Runs on Different Maps Your Work Introduces You Trial By Fire The Dude Why Headcount Math Lies Capacity Is the Roadmap The Roadmap Is Not the System Torres del Paine W Trek Escaping Status Theater Incentives Drive Everything Scaling Culture Without Dilution What Good Looks Like Why Airport Security Feels Random Why Politics Appear How to Work with Me The Janus Protocol Multi-Horizon Delivery Framework What Good Execution Looks Like Managing Your Manager Why Kingdom of Heaven’s Director’s Cut Is Better AI Broke Interviews Most of What We Call Progress Managers Have Been Vibe Coding All Along Stop Wasting Brainpower Why Over-Engineering Happens Prisoner's Dilemma Climbing No More The Weekly Win Mevlana Candy Brewing Turkish Tea Onboarding Your Engineering Manager Technical Deep Dives Yapay Zekâ Çağında Bilgisayar Mühendisliği Building Remote Teams From Idea to Launch in 2 Weeks Reflecting on Software Engineering Handbook Representing the Business New Manager Survival Guide Take Self Reviews Seriously Chasing Real Respect The Invisible Difference Learning the Johari Window Management is a Lonely Place Simple Task Management AI Balance in Work PIP Manager Insights Engineering Manager Interview Preparation Work-Life Balance as a Manager Bridging the Management Disconnect Tech Hiring Bubble Bursts Traits for EMs Simple Acts of Recognition Matter The Question I Ask Every New Report The Reality of an Employer's Market Bridging Ideals and Reality Hiring Red Flags Why The Godfather Is So Damn Good Subteam Tenets No Fluff Please Losing a Top Performer Balancing Act of Reliability Building Trust in Engineering Teams Ideal Number of Direct Reports Overriding a People Leader’s Decision From Misperception to Promotion Perception vs Perspective Setting Goals From Engineer to Manager Getting Delegation Right Interviewing Your Future Boss Celebrating Our Book in Iceland Operational Skills Needed On Writing Software Engineering Handbook Charlie Munger Quotes Working with Dependencies From Las Vegas to Canyons Navigating Layoffs Handling Competitive Dynamics A Weekend Getaway to Malta Engineering Health Essentials Should Dev Managers Code? Confronting the Life on Pause Winning Eleven Kindness is A Choice Bireysel Katılımcılar ve Yöneticiler Leading from Where You Are The Subtle Art of Listening Coding in Leadership The Power of Consistency The Making of a Leader The Path to Leadership Embracing TikTok Talent Sourcing Journey Leading Self Managing Teams Cracking Coding Bottlenecks
Compatibility Is a Feature
Yusuf Aytas · 2019-10-30 · via Yusuf Aytas

Published · 7 min read

A system only reveals what it's made of when it starts changing. At first, they often look better than they are because the code is still new. The data is still fairly clean. There is not much history yet. Most of the ugly parts only start showing up once the system has been in production for a while and people begin changing it.

What changed my mind was seeing how diffrent the same system looks once it starts changing under production load. A new version rolls out slowly. Some services are upgraded, some are not. A few clients keep old assumptions alive. Background workers process messages produced hours ago. Cached values stay around longer than you hoped for. Hope is not a good thing in this case. At that point, architecture has to answer whether old and new parts of the system can live together without causing trouble, or whether everything starts breaking in stupid ways.

That is why I think compatibility is a feature. I mean it in a very practical sense. If a system only works when everything upgrades together, then it is brittle. It may still look elegant in code review, but it will create stress the moment real change begins. A strong system has enough tolerance for mixed versions, delayed consumers, and partial rollouts.

Rollback Is Not a Safety NetRollback Is Not a Safety Net

Time is Part of the Design

The design is shown as if version N cleanly becomes version N+1. Production does not work like that. Rollouts take time. Queues introduce delay. Mobile apps stay old. Services restart at different moments. Data written by the new version is read by the old version.

While upgrading, the system has to deal with history, and that is usually where things get messy as hell. It has old data, old clients, and consumers that lag behind the producer. It also carries a bunch of undocumented assumptions that nobody gave a damn about when everything was still small and fresh. Plenty of designs look good when nothing is moving. The moment the system starts changing, you find out which parts were solid and which parts were bullshit.

Rollback Sounds Easier Than It Is

State is different. Once a schema changes, once records are stored in a new structure, or once downstream systems consume a new event format, the path backward becomes much less friendly. Sometimes it is possible. Sometimes it is slow. Sometimes it is dangerous.

If version N and version N-1 cannot work against the same state, then the rollback story is weak. The system has already crossed a one-way door. I remember working on a scheduling system and migrating it from a job-based model to a task-based one, where everything became a job and a job could have multiple tasks. I wrote the implementation in a week. Then I spent the next three weeks testing whether we could roll it back safely. The model change itself was not the expensive part. The expensive part was making sure the system would behave well during the overlap, when old and new logic had to survive each other.

Different parts Move At Different Speeds

Compatibility also matters because producers and consumers are usually not on the same clock. The team running the service wants to simplify things and get rid of old baggage. The teams consuming it have their own deadlines and their own mess. For them, upgrading means work, risk, and a lot of effort for something users may never even notice. So it slips. Then it slips again. That is normal.

The same thing happens inside message-driven systems. A producer starts sending a new payload. A lagging consumer is still catching up on the old backlog. Now both worlds are present in the same queue. If the consumer only understands one of them, failures start appearing in strange places. Retry loops, blocked partitions, and stuck processing pipelines are often signs that the compatibility window was not taken seriously enough.

I saw a similar problem while working on identity and tracking across multiple consumer products. Different apps, sites, and devices all had their own identifiers. Each one made sense locally. The hard part was keeping a stable mapping while the surrounding systems changed at different speeds. Cookies, device IDs, login IDs, and tracking IDs all had different lifetimes and different levels of trust. Once you try to unify them, compatibility becomes part of the design. The identity layer has to cope with delayed consumers, partial adoption, and merge rules without asking every other system to move at once. When that works, analytics and personalization get much easier. When it does not, the edges of the system start drifting apart.

Trouble Usually Starts at the Boundary

A lot of failures start right at the seam between old and new. The field is still there, but it means something slightly different now. The payload is technically valid, but the ordering changed and some downstream code starts choking on it. A new optional field shows up, and some consumer falls over because its parser was more brittle than anyone realized. Sometimes even the telemetry gets messed with during the migration, so the dashboard stays green while the system is quietly going to shit.

This is one reason production systems are harder than they look. The official contract is only part of the picture. Observable behavior matters too. If enough people depend on a behavior, it becomes part of the real interface whether you documented it or not.

Compatibility has a cost. You may need additive schema changes before removal. You may need dual writes for a while. You may need shadow reads, feature flags, or better telemetry on who is still using the old path. You may need to delay cleanup until you are sure the system has actually moved on. None of this feels especially elegant. It does, however, make production change much less dramatic. I am usually happy to pay that cost. A slower and safer transition is often cheaper than saving a week and discovering later that the whole release depended on perfect timing.

Before Changing a Live Boundary

Before making a structural change to a live system, I find it worth slowing down long enough to answer a few uncomfortable questions honestly.

Can the previous version still run safely against the new state? Not in theory, but in practice, with real data written by the new code. Can delayed consumers survive the new payload, including the ones sitting behind a backlog that is hours deep by the time they catch up?

I also want to know whether additive changes have been separated from destructive ones. Mixing them is how a cautious migration becomes a fragile one. And before removing anything, I want to know who still uses the old path, based on telemetry rather than memory.

The last question is the most important. If the rollout goes badly, do I have a real rollback, or only a code rollback? A code rollback that cannot safely read existing state is not a safety net unfortunately. I have seen many systems where the rollback was not possible because the new code changed the state in a way that the old code could not read.

None of this is especially fast. But I would rather answer these questions before a release than during an incident.

In Consequence

Compatibility is one of the things that makes production change manageable. It gives you room for old clients, delayed consumers, mixed versions, and rollback decisions that still work against real state. Once you start designing for that overlap, the system usually becomes easier to trust, because you are finally designing for the way it actually changes.