BOOK DEEP READ · CONTINUOUS DELIVERY · CH 12

Managing Data

Continuous Delivery · Ch 12 · Jez Humble & David Farley · 2010

中文 →

What is this chapter about?

Behind every app you use sits a database holding your orders, your messages, your balance. Earlier chapters covered how a new version goes live and how you take it back if it goes wrong. This chapter is about the part you cannot take back: a new version usually needs to change how the data is stored, and data, unlike code, has no "previous version" to swap back to.

An analogy

Think of the database as the house numbers on a street. Shipping a new app version is like changing a shop's sign — hang the wrong one and you take it down again, five minutes' work. Changing the database is like renumbering the whole street: the courier's address book, everyone's ID cards, the delivery platform's records all still point at the old numbers. Rip every old number plate off overnight and the next day the whole street's parcels go astray — and the parcels already sent to the new numbers cannot be recalled.

Why the old way was hard

The old way went like this: on release night someone who knew the database logged in and typed a few commands by hand off a scrap of paper. And when it went wrong? There was usually exactly one answer — restore the whole database from last night's backup. That sounds safe, but it means wiping out every order, payment and message anyone made since last night. So teams preferred not to change anything, hoarding six months of changes into one big release, then spending a nervous Saturday night at 3 a.m. pushing it.

What fixes it

First, write every database change as a numbered work order, stored in version control next to the code. The database remembers "I am at number 27", and on release it compares and applies 28 through 31 in order. Who changed what is on the record, and the same work orders have already been rehearsed many times in test environments before they reach production.

Second, work orders only add, never demolish. To retire a field, add the new one, copy the data across, and leave the old one in place — so if you need to go back, everything is still there.

Third, and this is the key move: keep two number plates up for a while. There is no longer a single moment of switchover. Instead it becomes several small steps — hang the new plates first and let old and new coexist for a period; once everyone's address book has been updated, take the old plates down at your leisure. Any one step going wrong means stopping there and stepping back, not tearing up the whole street.

And then there is test data

Plenty of teams take the shortcut of copying the entire live database into their test environment. It looks like the most realistic option and it is the most damaging one: it is huge and slow, it is full of real people's private information, and anyone can change it — someone deletes a record today and tomorrow somebody else's test fails for no visible reason. The sturdier approach is to let each test create the handful of records it needs and clear them away afterwards.

The cost you cannot dodge

Keeping two number plates up means a simple change now takes several separate releases, and for a while the code has to serve both the old and the new shape — more steps, more fuss, in exchange for being able to stop and step back at every one of them.

The one-line takeaway

Code can be swapped back to the previous version; data cannot. So: write every database change as a numbered, versioned script that has been rehearsed across every environment; only add, never demolish; and split a structural change into several steps where old and new coexist, so every step can stop and step back.

Want the mechanics — how migrations get numbered, how the six steps of expand-contract actually run, what data each pipeline stage should use? → Switch to the deep read