Recently, I’ve been doing some thinking about some of the fundamental design concepts I’ve learned as a software engineer. In what follows, I want to share some of my thoughts about two particular design architectures. I want to voice some of the things that I’ve been thinking about in relation to these architectures, discussing what I like about them and what I don’t.
The thing that was hammered into my head as a CS student at OSU was that there is not necessarily a right answer to the architecture question. It’s all about tradeoffs and about what serves the team’s needs best.
Two years into my engineering career now, I’ve developed at least the seedlings of some thoughts about the discussion.
Observing from the last company I was at, we had really nothing but microservices. Everything was broken into a separate cloud service that was stored and maintained in separate repositories. Many of these repositories were maintained by different teams that I had really no contact with. What I’ve seen from my time there was that these microservices were pretty useless because of the lack of communication between teams. We didn’t know who to talk to about which service. It was made all the more confusing because the transition to microservices was only half complete. All of the old services that lived in the monolith of each application was left there, but not marked as legacy code!
I literally couldn’t believe that there was exact replicas of the microservices just left in the monolith repositories. What made this situation even worse was that we had no way to know which version was the currently maintained one. Was the microservices the newer version or was it the monoliths? Were we moving towards microservices or away from them?
On the flip side, the team I work on now has a monolith repository, but a microservices backend. Everything is stored in one place because the project is, in theory, small enough to manage all in one place. But there are dozens of microservices. The difference with my company now and the company I used to work for is that there is at least some documentation and communication about what microservice does what.
We’re a really small dev team. Essentially, there is only myself and two other engineers, one who is the lead dev and the other is a back-end dev, who is a contractor and who built-out most of the architecture. What’s nice about this arrangement is that almost all of the knowledge about what does what in the code base lives within the team. Moreover, this information is fairly well-documented, or, at the very least, there’s some sort of git commit trail or release notes, detailing what got implemented and at what time. This is indescribably better than the situation at my last company!
I think the key point that I’m driving at with these examples is that, making decisions about software architecture is only half the battle. Along with this decision, a company must also decide on team structure and communication methods. A team needs to know where the information is and who has this information.
When this is implemented correctly, you end up in a situation like my current team. If I have a question about which microservice I need to call in the client, I know exactly who to talk to. I also have a diagram that details the architecture in very minute detail. I know what does what. And, probably more importantly, I know who does what.
Another observation that I’m beginning to make is that it’s important to build things in such a way that we understand our services at every step of the build-out process. As we build things out, things will change. Services will change. Yet, we need to have some way to continually maintain transparency about what our services are doing. We need some way of making sure that everyone knows exactly how things are working in the current versions of a services. We also need some way to make sure that everyone understands how things have changed over time. Along with understanding how things work now, team members also need to understand how things used to work, so that they understand the context that they’re working with. We can’t just silo teams and expect everyone to stay updated about what every other team is doing. There needs to be constant communication throughout the process and across different teams.
Evernote Example
Outside of my own work experience, I found a really clear example of these principles being implemented correctly. A few years ago, Evernote made some really fundamental changes to their core software’s infrastructure. To detail all of the changes they made is beyond the scope of this article, but here’s a brief overview of what changes they made:
Previously, Evernote had an old Java Enterprise Edition (JEE) application that ran the bulk of its business logic. That old application powered “the storage and synchronization of multiple entities, including notes.” Evernote also used MySQL as their primary database engine. They noted that the database was running on the same machine as the application layer.
So, essentially the upshot of all this is that the old architecture at Evernote was built as a monolith and had really slow database performance. Inevitably, the product infrastructure was extremely heavy (i.e. large amount of code) and difficult to manage (i.e. difficult to reason about and untangle). The core software product at Evernote was becoming really slow and lacked in performance.
As a side note, coincidentally, I actually started noticing this slow-down in performance a few years ago and stopped using Evernote because of this. I was previously using Evernote to house all of my college note-taking, but I noticed that once my notes reached a critical size, Evernote stopped being snappy and easy to use. It took so long to load my notes, that I figured it was easier just to take notes on paper.
Needless to say, Evernote noticed this problem and took steps to address it. Over the course of few years, they eventually turned their outdated monolith into a series of more performant microservices. They also introduced numerous new data structures and implemented various new patterns for search and information storage. Again, to get into the weeds of how this all works is beyond the scope of this article.
The main point here is that, Evernote made this transition in a fairly smooth manner. Reading through their engineering blog, it’s pretty clear that they were careful about this transition. They have numerous articles detailing the steps they took and how they went about rolling out these changes. This, in my opinion, is a great example of architecture decisions paired with clear and meaningful communication along the way.
Well done, Evernote!
Resources:
- https://evernote.com/blog/future-proofing-evernotes-foundations