"Let’s say you have an application that handles the POS (Point-Of-Sale) for a company. Then some law change and you have to apply a new tax for all the people who buys only beverages with alcohol. Wines and beers will pay 2% additional tax, vodka, whisky, rum and all that kind will pay 5% tax. Your application only handles a regular fixed tax applied to some items and some not.
- You will need to add to your catalog the tax and their percentages.
- Make your items catalog can handle this tax.
- Modify the POS to handle and print the new tax.
- Modify the cashiers report at the end of his shift.
- Modify all the reports to now show the new tax according
How can this be done without using a long lived feature branch where all this is implemented and tested before it is merged into the master branch? It would obviously be disastrous if we put only some of this functionality into production, customer might pay too much, or we might break the law. Neither is any good.
I wrote a short article to describe how to do TBD, Trunk Based Development (Trunk Based — A How-To). But it doesn’t really answer the concrete challenge POS mentioned above, so lets go into more detail and see how this can be solved.
The first step is updating the database. Databases are a pain to change. It’s also not very easy to check for feature toggles when determining what columns should be available in a table. We need to do this some other way.
When putting new code into production, usually a lot of time is spent migrating the data in the database to the new schema. At times I have had to stop a production upgrade because converting the database was too slow. These things are usually difficult to detect and test up front, since they often are caused by inadequate indexes and large data volumes.
Another pain is that schema changes are difficult to roll back. That’s why I usually have a full back-up of the database. But that’s time and space consuming. And sometimes you might not have the time nor the space.
As usual, when something is slow, cumbersome and difficult, the solution is to do it more often.
Doing it often makes each change small. And small changes are easy. It also means we will automate the process, making it even easier.
Having a database change management tool like Flyway or Liquibase can be quite helpful. They will keep all modifications with the tagged source code and the versioned binary. That way the database matches the code at all times.
The trick is to keep the database backwards compatible at all times. That means we can only add stuff. Tables and columns cannot be dropped. Columns cannot change type. These are breaking changes and will make the old version of the software crash on the new database. Of course there are some exceptions. If a type change doesn’t modify the corresponding type in the application, it is allowed. Ie. increasing the length of a VARCHAR-column. Tables and columns can be dropped when they are no longer in use. Usually it’s smart to wait a couple of versions for this.
Note that this is just plain common sense with regards to databases. It isn’t unique to TBD. It is, however, a requirement for TBD.
So for the challenge, we need to add a column to the product table to hold the additional tax. We could add a new table instead. This depends on the actual database and architecture in question. No matter what, we add something to the database schema.
This change can be put into production right away.
There is no code using it, so it cannot make the system fail. We can even add values for known items, if there’s an easy way to identify groups of products, or a source we can import from.
For the code, we need a feature toggle. That should be easy, let’s just call it “New Alcohol Tax—2020”. Whatever mechanism we use to fetch the toggles, it should return ‘false’ for this newly introduced toggle.
Now we need to update the screens where administrators manage the product catalog. These must have a new field available where the new tax information can be added and modified. In the HTML, this is a DIV that’s only visible when the “New Alcohol Tax — 2020” is true. We might enable this in a test environments to discuss placement with a product owner. Or we just enable it on our development machine while developing.
This code can be pushed to production. The toggle is at the most enabled in a test environment for a specific user, so no users will see it in production.
Once we have the UI in place, we can write the necessary code to transport the values to and from the database. If we need to, we add if-tests for the feature toggle. This might not always be necessary, if the values are never used, it doesn’t matter if we fetch them. Only when we start doing calculations with the new tax values, we need to check the feature toggle.
At this point, I hope you can see where we’re going.
We can now add code to handle the new tax in the POS and modify the various reports. These changes are done inside an if-test checking the feature toggle “New Alcohol Tax — 2020”. We turn this toggle on and off in our various test environments and for our various users as needed. The code goes into production, but is never run there, since the feature toggle there is off. Depending on the architecture of the application, it might be possible for several developers to work in parallel. Each part can be tested by business people and testers, quite possibly even though the full implementation is not done yet.
The database is a special here. There are no code paths in a database, so we’ll just have to accept a couple of extra columns here and there. If a new column needs an index, it might be smart to hold off adding that index until there’s a useful amount of actual data in the production database. But that will depend on the database, the index, and the test data you add.
At some point, all the code is written, and we can enable the toggle for our QA-people, accountants or whoever needs to verify the new functionality. We can add a special shop in our production database, one that isn’t real. If we enable “New Alcohol Tax — 2020” there, we can actually verify the functionality in our production environment without affecting everybody. It is also possible to use an existing shop for piloting the new calculations.
As we add code, we obviously also add tests. The existing tests handle the case where the feature toggle is off. The new tests must then run with the feature toggle on. To make this work, we can add a special user for these tests. That user then has the feature toggled on. It is also possible to add the feature toggle as a header parameter in our REST-API. Or we can use a combination, whatever is the easiest.
That’s really all there is to it.