// Memes

Tech debt, drawn in code

The software lifecycle, one picture at a time. Every image here is drawn by code — SVG built from TypeScript, no stock art. Share freely; a link back is appreciated.

✓ the feature we demoed// TODO: remove after migration (2018)3 feature flags stuck at 100% since 2019the cron job that quietly fixes prod dataone engineer who understands billinga table two services both write tonobody knows what this does. don't touch it.Tech debt: you only ever see the tip.ayushwing.com
The tech debt iceberg
retry()retryGET /stockretry()retryGET /stockretry()retryGET /stockretry()retryGET /stockretry()retryGET /stockretry()retryGET /stockretry()retryGET /stockretry()z z zrecovering…database(needed 1 minute)3 services × 3 retries each= 27 calls per user requestA database that needed one minute,meeting three layers of retries.ayushwing.com
The retry storm
partition 0partition 1partition 2partition 3partition 4partition 5user-7user-19user-3user-88user-51cust-42cust-42cust-42cust-42cust-42cust-42cust-42consumer lagKafka keeps order per partition.So does the traffic jam.ayushwing.com
The hot partition
MONOLITHrefundsemailsearchwalletBIG-BANGREWRITE, 2019Don't rewrite the monolith. Grow around it.ayushwing.com
The strangler fig
Team A: v2 API (REST)Team B: v2 API (gRPC)↕ 70 pxIntegration: approvedin standup ✓Two teams, one integration, zero design docs.ayushwing.com
The bridge
REFUND SERVICERequest timed out.RETRY ↻₹1,499refund #1₹1,499refund #2(already paid)(paid again)The response was lost. The refund wasn't.ayushwing.com
The double refund
customersphone(2014, "temporary")phone_e164(2023, the real one)✓ expand✓ dual-write✓ switch reads☐ contractExpand ✓ Migrate ✓ Contract: next sprint.(since 2023)ayushwing.com
Two columns, forever
our job system, two years later⏰ cron*/5 * * * *jobs tablestatus VARCHARworkerThread.sleep()status = STUCK?nobody knowsunstick.shrun by hand#ops-help"is job 4512 done?"retry_count++until 5, then?DONE ✓(sometimes)Every queue + cron + status column becomesa workflow engine. A bad one.ayushwing.com
The job machine
order #1042status: PAID(for now)cancel()ship()t = 0 mst = 50 mssaw PAID ✓saw PAID ✓Both checked the status. Both were right.Use a conditional write.ayushwing.com
The race condition
PostgresMongoDBRedisElasticNeo4jKafkatodo-appmonthly users: 1A to-do app, built for scale.ayushwing.com
Polyglot persistence
09:00deployDB CPU40%cloud statusall greennoisyneighbour?certexpiry?rolled backstill brokenon-callDNSEvery incident review, eventually.ayushwing.com
It was DNS
SELF-SERVE PLATFORMnew envdatabaseaccessdeployDNSbucketsecretqueueNOWSERVING#4512est. wait2 sprintsJIRAfill in 14fieldsthere is aperson inside→Self-serve, as experienced by every other team.ayushwing.com
Self-serve
PMcamera offEMcamera offQAcamera off@McpTool attend_meetingSounds good,let's take thisoffline.me, actuallywriting code(headphones on)Leave meetingFinally, an MCP tool that attends meetings for me.ayushwing.com
The meeting agent
0d2d4d6d8d10d0.5d2dfix a typo1d4dadd one field1d6dbump a library0.5d14d ↑"should be quick"Estimate vs actual, daysestimateactualEvery sprint, plotted honestly.ayushwing.com
Estimate vs actual
R.I.P.my demo linkfree dynoNov 28, 2022"it's free, what could change?"Anything you don't pay for can disappear.ayushwing.com
RIP free tier
CI · refund-serviceall checks passedrefundsTwiceIsFine()refundWorks()refundWorksAgain()refundWorksV2_final()edgeCaseMaybe()testTest()100%coverageRefundServiceTest.java@Testvoid refundWorks() {var r = service.refund(o);assertNotNull(r);assertTrue(true);}asserts: nothinggenerated in 4 secondsAI wrote the tests. All green.Read every assertion anyway.ayushwing.com
The generated tests
database✓ order committedkafka✗ never publishedOrderCreated(gone)kill -9(deploy, mid-request)Commit, then publish.What could possibly go wrong?ayushwing.com
The lost event