9

My company* wants

  1. to move from using long-running feature branching (~up to a few weeks) to continuous integration with trunk-based development, and
  2. to break up our monolith into microservices.

We're unsure which to do first to best achieve both: 1) moving to trunk-based continuous delivery, albeit dealing with a several-hour time delay for our automated testing in the meantime; or 2) splitting up our monolith to shorten build and test suite times, then moving those to trunk-based continuous delivery.

Is there consensus on the appropriate order of operations here? If not, what are the pros and cons of implementing one first while trying to move towards the other?

*For context, our main application is a large .NET monolith released weekly, with a lead time for changes of about 2-4 weeks to accommodate long feature branches and end-of-sprint QA testing, a robust unit testing suite that takes ~2 hours to run fully, and a longer-running set of integration tests maintained by a separate QA team.

EDIT: We seem to be running into some terminology issues.

By continuous integration, I mean a practice whereby all developers working in a common code base are getting each other's changes all together at least once per day. The use of feature branches lasting longer than a day is incompatible with CI in this sense.

By continuous delivery, I mean CI combined with version control for all production artifacts, automated build & test pipelines, and prioritizing fixing the build over other work if it breaks, so that we aim to always keep our main branch in a deployable state.

By continuous deployment, I mean continuous delivery combined with automatically deploying releases passing all pipeline checks to production. This question does not ask about continuous deployment.

This is how the terms are used by Jez Humble and Dave Farley in their 2010 book Continuous Delivery first popularizing the topic, in Martin Fowler's bliki, in the DORA research group's State of DevOps reports, in Accelerate, and in Dave Farley's Continuous Delivery YouTube Channel.

12
  • 2
    A critical nuance to consider: Are your unit tests so entangled that they will become simpler under your microservice plan? This will tell you whether CI will become faster under microservices, which will tell you whether microservices-first is worthwhile.
    – Corbin
    Commented Jun 4, 2023 at 16:37
  • 2
    I would recommend to replace "CI/CD" by "CD/trunk based development" in the title's question, assumed that this is what the question is about. This would make clear if the currently top-voted answer is on track or missing the point. I suspect it is the latter, but you should really clarify this.
    – Doc Brown
    Commented Jun 4, 2023 at 21:01
  • 3
    CI/CD doesn't mean no feature branches. Just to be clear; are you suggesting short term (days) feature branches and frequent merges into trunk with code review (fine) or literally every commit goes into trunk and straight out to prod (which feels like it needlessly ties your hands) Commented Jun 5, 2023 at 11:36
  • 4
    Note that you may be able to simply parallelize the unit testing. How big is the development team? Microservices is often an antipattern when there's only one team.
    – pjc50
    Commented Jun 5, 2023 at 11:48
  • 1
    Have you seen Dave Farley's YouTube videos about microservices?
    – benxyzzy
    Commented Jun 6, 2023 at 5:24

5 Answers 5

27

Consensus is you want CI/CD first and anyway, independent of your application's language, design or architecture. Whether you deliver trunk-based or use feature branching is also independent of CI/CD.

CI/CD is just automating your build steps, testing and packaging which is convenient to have for any software product under development. You typically want to set that up before you do any development, right after you created the first empty module in your application.

So it seems you are long overdue with that. But again, it is not really an A vs B question, they are independent.

10
  • 6
    To some extent, I'd say "build tests, testing and packaging" is CI but not CD; for it to be CD, you also have to be releasing on every commit to main or similar. Commented Jun 4, 2023 at 7:35
  • 1
    @PhilipKendall I was unsure about this. "Main" sounds like a database centric type of system to me by the way. Would you consider automatically creating an installer not to be CD? Would you say it had to include some kind of deployment to a usable environment? Commented Jun 4, 2023 at 8:20
  • 2
    I'd personally say CD requires deployment to a production environment, but I admit that's a view which is biased towards my recent jobs which have been SaaS-type things where you can do that. Commented Jun 4, 2023 at 8:46
  • 8
    The issue here is that some people use CD for Continous Delivery, some other mean Continous Deployment.
    – Doc Brown
    Commented Jun 4, 2023 at 9:09
  • 5
    I guess the OP already uses CI in the sense of this answer. They use CD as a synonym for "trunk-based development", which makes most parts of this answer quite pointless.
    – Doc Brown
    Commented Jun 4, 2023 at 10:15
12

Trunk based development with continuous develivery and microservices are mostly orthogonal concepts. However, for a huge monolith, the delivery cycles which usually require a certain amount of testing will typically last longer, which will work againts the idea of CD.

In theory, having smaller microservices which can be developed and deployed independently can shorten those cycles. In practice, this will only work if you manage it really up to the point where a new release of microservice X has a low risk of breaking the whole system, and you can indeed reduce the necessary amount of integration testing for all other microservices here. For this, X must be strongly decoupled from other parts of the system, and have a certain tolerance against changes of those parts, and those other parts must have also be robust against changes of X.

What does that mean for your order of transition? I suggest you try the following:

  • Identify a part of your system you want to make a (micro-)service of its own. Create the necessary interfaces around it and rework it up to the point to be able to deploy this independently of the rest of the system. This includes a certain backwards and forwards compatibility of the versions of both parts now.

  • This new service gets a repo of its own and you use trunk based development for it (instead of feature branching).

  • Repeat this until the remaining core part of the system has become so small that you perceive it as a microservice of its own, and you can switch to trunk-based development for it as well.

Of course, in reality, things will probably not turn out to work so straightforward. But the gist of my recommendation here is, try to approach this iteratively and observe where it leads you. Breaking your architecture down into micro services can embrace trunk based development, but trunk based development does not necessarily require your product broken down into microservices in full.

2
  • The likes of Dave Farley would say that having CI in place first will make the breaking down into microservices process quicker, because automated tests can catch regression etc. WRT microservices, the world is long into the post-hype disillusionment stage now, majority opinion says you'll tend to end up with even more unmanageable complexity. So you will be very grateful for the tests (+documentation of contracts, interfaces etc. to the extent that they achieve this) when you are mired in the transition
    – benxyzzy
    Commented Jun 6, 2023 at 5:21
  • 1
    @benxyzzy: the OP has already those CI techniques in place, the question's title is misleading (and the top voted answer missed that completeky). My answer is about trunk based development and microservices.
    – Doc Brown
    Commented Jun 6, 2023 at 6:26
3

Moving to trunk based development while still being on the monolith could also work. You would need to ensure that dev branches are short-lived and merged to main & integration tested often (ideally within a few days).

Note that splitting up the monolith into microservices could take time (i.e several months to over a year).

1
  • 1
    Yes, that last point is important. Adopting continuous integration is something that can be done relatively quickly... not overnight, but it's simply a process change. Splitting up the monolith is a much harder and much more time-consuming task... depending on the size of the monolith, "several months to over a year" may be highly optimistic... Commented Jun 5, 2023 at 21:54
0

Consider refining your CI/CD approach by building the first microservices.

In my experience working with "legacy systems", it can be very hard to retrofit a better build process and add on automated tests. That depends on how old your system is, and how complex, of course. You may find that your new approach to automated testing, or a new way of scripting the build, doesn't work for reasons that are hard to figure out (undocumented dependencies, for example).

On the other hand, your team is probably taking some training or reading some books about microservices and CI/CD, and working through little examples as part of that learning. I suggest that the next step would be to put your new skills/ideas into practice by building a very simple but usable microservice, with the automated tests, build process, and CI/CD process you want. Experiment and tweak until everyone is satisified, then make the old monolith consume that microservice instead of doing that functionality itself.

The benefit of this approach (doing both new things together, but in parallel with the existing monolith until it's ready) is that you can come away from it with a DevOps model that everyone on the team is happy with and can reproduce many more times.

On the contrary, if you attempt microservices first and then later attend to the build/test/deployment pipeline, you may find that your microservices design wasn't quite right and needs to be re-engineered. Or if you develop your build/test/deploy pipeline first and then try to split it up into microservices, you may find that you need to backtrack and adjust the first thing.

0

In a way these two aims are contradictory, or at least going in different directions.

Trunk based development is about frequent integration of the work from different developers - integration meaning merging together and then running automated tests and builds on the combined part.

Moving to microservices is the opposite - it separates out work from different developers so that they never have to be integrated. They are deployed separately, and the first time a specific pair of versions meet may well be in production.

I think the most useful thing is to move towards Trunk Based Development and Continuous Delivery within the monolith.

If you still find you want or need to move towards microservices, start small by just splitting off a single microservice from the monolith. Once you've had time to evaulate that decide whether or not you want to create a second one.

Not the answer you're looking for? Browse other questions tagged or ask your own question.