49

Another way of asking this is; why do programs tend to be monolithic?

I am thinking of something like an animation package like Maya, which people use for various different workflows.

If the animation and modelling capabilities were split into their own separate application and developed separately, with files being passed between them, would they not be easier to maintain?

dnv
  • 591
  • 1
  • 4
  • 7
  • 9
    `If the animation and modelling capabilities were split into their own separate application and developed separately, with files being passed between them, would they not be easier to maintain?` Don't mix *easier to extend* with *easier to maintain* a module -per se- isn't free of complications or dubious designs. Maya can be the hell on earth to maintain while its plugins are not. Or vice-versa. – Laiv Mar 12 '19 at 13:24
  • 37
    I'll add that a single monolithic program tends to be easier to *sell*, and easier for most people to *use*. – DarthFennec Mar 12 '19 at 16:34
  • 2
    @DarthFennec The best apps look like one app to the user but utilize whatever is necessary under the hood. How many microservices power the various websites you visit? Almost none of them are monoliths anymore! – corsiKa Mar 12 '19 at 16:38
  • 23
    @corsiKa There's usually nothing to gain by writing a desktop application as multiple programs that communicate under the hood, that isn't gained by just writing multiple modules/libraries and linking them together into a monolithic binary. Microservices serve a different purpose entirely, as they allow a single application to run across multiple physical servers, allowing performance to scale with load. – DarthFennec Mar 12 '19 at 16:50
  • 5
    @corsiKa - I would guess that overwhelming number of websites I use are still monoliths. Most of the internet, after all, runs on Wordpress. – Davor Ždralo Mar 12 '19 at 19:52
  • 2
    Just bear in mind that distributed computing is not an end. It's a mean to an end. A mean that usually comes by the hand of the fallacies of the distributed computing. And trust me, they are not anything remotely close to "easy to maintain" or "easy to deal with" friend. – Laiv Mar 12 '19 at 20:41
  • Mainly it annoys users who have to keep switching between animation and modelling programs, importing and exporting data. I find this is pretty obnoxious in, for example, KiCad - an electronics design tool that has different programs for editing schematics and editing PCB layouts. – user253751 Mar 13 '19 at 01:51
  • @DavorŽdralo Arguably, "most of the internet" consists of a lot of shitty websites you (and the majority of users) _do not_ use. The bigger sites you (and the majority of users) _do_ use _probably_ use CDNs. Also any kind of webshop will have to use 3rd party payment portals. Just two things of the top of my head. – R. Schmitz Mar 13 '19 at 10:37
  • 2
    @R.Schmitz - how is that in any way related to anything I said? Using third party services or distribution networks does not, IN ANY SENSE IMAGINABLE, make your application not a monolith. – Davor Ždralo Mar 13 '19 at 11:00
  • @DavorŽdralo Easy, I demonstrated how Wordpress still fits the description corsiKa gave, which _you_ replied to. If you think that using a different application on a different server - heck, literally from a completely different company - makes a monolithic application, then OK, we'll just agree to disagree. I see how your statement makes sense from your perspective. – R. Schmitz Mar 13 '19 at 11:17
  • 2
    Smaller apps are easier to maintain individually but there's an additional burden of making them communicate better and testing them separately. So in my mind there is probably a net increase in maintenance in some situations. But not always. If the monolith is terribly written then separating it out might be a net win. The advantage of microservices is more in scaling than in maintenance. The maintenance feels generally higher on average. – Mark Rogers Mar 13 '19 at 15:07

10 Answers10

96

Yes. Generally two smaller less complex applications are much easier to maintain than a single large one.

However, you get a new type of bug when the applications all work together to achieve a goal. In order to get them to work together they have to exchange messages and this orchestration can go wrong in various ways, even though every application might function perfectly. Having a million tiny applications has its own special problems.

A monolithic application is really the default option you end up with when you add more and more features to a single application. It's the easiest approach when you consider each feature on its own. It's only once it has grown large that you can look at the whole and say "you know what, this would work better if we separated out X and Y".

Peter Mortensen
  • 1,050
  • 2
  • 12
  • 14
Ewan
  • 70,664
  • 5
  • 76
  • 161
  • This. Also it’s easier (and usually more effective) to separate one functionality that has specific needs (in terms of computer power for example) rather than split everything as a greenfield project. – Steve Chamaillard Mar 12 '19 at 13:11
  • 6
    Yes and there are also performance considerations e.g. the cost of passing around a pointer versus serializing data. – JimmyJames Mar 12 '19 at 14:31
  • 65
    *"Generally 2 smaller less complex applications are much easier to maintain than a single large one."* - that's true, except, when it is not. Depends heavily on where and how those two applications have to interface with each other. – Doc Brown Mar 12 '19 at 16:29
  • Except systemd. That is better as large complex application encapsulating a myriad of functionality and dependencies. – paulj Mar 12 '19 at 17:38
  • 10
    "Generally 2 smaller less complex applications are much easier to maintain than a single large one.". I think I'll want some more explanation for that. Why exactly would the process of generating two instead of one executable from a code base magically make the code easier? What decides how easy code is to reason about, is how tightly coupled it is and similar things. But that's a *logical* separation and has nothing to do with the *physical* one. – Voo Mar 12 '19 at 17:46
  • 1
    @Voo + doc _"generally"_ is obviously a key word here. I would say that given all other things being equal the physical separation _forces_ and guarantees a logical separation which might not otherwise exist, or even if it did exist might not be provable from a black box testing senario – Ewan Mar 12 '19 at 18:22
  • 11
    @Ew The physical separation does not force a logical separation, that's the problem. I can easily design a system where two separate applications are closely coupled. Sure there's some *correlation* involved here since people who spend the time to separate an application are most likely competent enough to consider these things, but there's little reason to assume any *causation*. By the same logic I can claim that using the latest C# version makes code much easier to maintain, since the kind of team that keeps up-to-date with their tools will probably also worry about maintenance of code. – Voo Mar 12 '19 at 18:33
  • 1
    if you did that application would fail all its tests because the dependent application would not be running – Ewan Mar 12 '19 at 19:10
  • 5
    While to a certain extent the idea of a "client/server" model for a single application has some interesting upsides (a pseudo protocol contract between binaries), from my experience the OS's inter-process communication APIs tend to have more overhead and are also harder to work with than working with libraries (which have a shared addr space). Unless one of your applications is useful to your audience standalone (and/or able to be coupled on the fly, like a dedicated server or utility program), I would recommend having a single application with dependencies in static/dynamic libraries instead. – jrh Mar 12 '19 at 19:48
  • 3
    @Ewan If I did what? You're claiming that there's some inherent reason why separate applications have a lower coupling without providing any argument for why that should be. Now if you wanted to argue instead that a program where separate parts can be easily tested in separation I'd certainly agree that such a program would be easier to maintain. But there's nothing inherent in a microservice architecture that magically makes it easier to test. This is a classical case of confusing [correlation with causation](https://xkcd.com/552/). – Voo Mar 12 '19 at 20:16
  • 1
    @Voo I do mention the orchestration problem. But you cant get away from the fact that separate applications reduce the over all complexity. Each app has a defined surface, this reduces the total number of possible errors if you look at the app as a black box. Sure logical separation inside an app can add more testable surfaces. If you assume that A those separations are correctly implemented and B the compiler prevents a programmer from crossing those internal boundaries. – Ewan Mar 12 '19 at 20:39
  • @Voo the equivalent assumption with two apps would be that one loads the others executable and makes unsupported calls into its functions. Possible, but highly unlikely – Ewan Mar 12 '19 at 20:43
  • 2
    Another kind of conceptual "feature" of a monolithic application is the program counter itself is state; as in, the order of steps is trivially guaranteed. If you decide to split up a program you lose that synchronization, which may or may not be of value; for example, if Module A does something, then Module B does something when A is done, then Module A does something after Module B again, instead of advancing line by line through a function (the program counter) you are now forced to have an IPC synchronization at each step. Startup code will likely have to address this challenge. – jrh Mar 12 '19 at 20:52
  • 1
    @Ewan Depending on your chosen technology there can be literally no syntactic difference between a local method call and a RPC. Yes physical separation can be helpful to find the right abstraction layers and bounded contexts for your problem but there are other ways to achieve the same result. The important things are your APIs, whether you do RPC, http calls or in memory calls is an implementation detail. – Voo Mar 12 '19 at 21:59
  • 3
    The reason I'm that focused on this is because I've seen many teams that decide their next product has to be "microservices" fall into exactly that trap. They assume that since they now have three dozen physically separate processes running on the same machine that are deeply interconnected and require synchronous calls everywhere that they have somehow improved their architecture. I've seen "monolithic" code that was vastly better designed and separated than these kind of microservices. Invest your time in thinking about bound contexts and it doesn't matter how many processes you have. – Voo Mar 12 '19 at 22:03
  • @voo correct. its not about the number of processes its about the testable boundaries. You can fully test 2 applications with less tests than would be required for the same functionality in a single app. Simply because the bits which _are_ separable can be proven to be separate. Put it all in one app and you have to assume there can be cross over when testing that app. – Ewan Mar 12 '19 at 22:16
  • sure if every bit of your app calls every other bit then yeah, the overall system is the same whether you are calling apis or libraries. But _generally_ that isn't the case – Ewan Mar 12 '19 at 22:18
  • 1
    if i have an app that emails and prints for example, I have to assume that it might not email if it previously printed if i have an email app and a print app _that don't talk to each other_ I dont have that concern – Ewan Mar 12 '19 at 22:21
  • The way to manage a large monolithic program is to organize it. I personally like the "app" approach, where you have eg. ``apps`` directory in the root of the project, then inside it you have further directories like ``users``, ``auth``, or ``contactform``. And inside each directory is the implementation of the feature mentioned in the directory name. All models, controllers, resources, etc. of that single feature will be under that one directory, and each feature has their own set. It is really easy to manage, develop and understand. I've used it in Python, Node.js, PHP and React projects. – Juha Untinen Mar 12 '19 at 22:22
  • 1
    @Voo Having two separate applications that do not communicate or interface to each other rather than one larger application with the features of both means that you don't have to worry about all of the extra code allowing you to switch between those two features and any other user interface code necessary to package them together that would otherwise be avoided if they were two completely separate desktop applications. So in that sense they are simpler because you'd eliminated a ton of junk. – user64742 Mar 13 '19 at 02:02
  • 1
    @Voo Plus you then eliminate the potential for a code path for one feature interfering with some data used for a different feature do to some unusual bug. (for instance an animation editing tool somehow getting enabled in modelling mode and causing the model to shear in half). That can't happen if the executable is just the modelling mode with no ability to switch to an "animation edit mode" that is instead bundled in a separate program you can just switch to in another window. Of course, anyone needing to switch often will absolutely hate that software and never use it. – user64742 Mar 13 '19 at 02:04
  • 9
    I think the discussion here can be summarized with 2 statements: 1) Splitting an app itself does not make an app more maintainable - on the contrary, it provides another possible point of failure 2) Splitting an app _forces_ you to think about where to split it, which provides an advantage compared to a monolith where that has never been done. – R. Schmitz Mar 13 '19 at 11:22
  • 1
    I would strongly recommend against splitting apps / an existing codebase, unless the apps themselves can also work standalone on their own without the second one. – Gizmo Mar 14 '19 at 09:47
52

Does splitting a potentially monolithic application into several smaller ones help prevent bugs

Things are seldom that simple in reality.

Splitting up does definitely not help to prevent those bugs in the first place. It can sometimes help to find bugs faster. An application which consists of small, isolated components may allow more individual (kind of "unit"-) tests for those components, which can make it sometimes easier to spot the root cause of certain bugs, and so allow it to fix them faster.

However,

  • even an application which appears to be monolithic from the outside may consist of a lot unit-testable components inside, so unit testing is not necessarily harder for a monolithic app

  • as Ewan already mentioned, the interaction of several components introduce additional risks and bugs. And debugging an application system with complex interprocess communication can be significantly harder than debugging a single-process application

This depends also a lot on how well a larger app can split up into components, and how broad the interfaces between the components are, and how those interfaces are used.

In short, this is often a trade-off, and nothing where a "yes" or "no" answer is correct in general.

why do programs tend to be monolithic

Do they? Look around you, there are gazillions of Web apps in the world which don't look very monolithic to me, quite the opposite. There are also a lot of programs available which provide a plugin model (AFAIK even the Maya software you mentioned does).

would they not be easier to maintain

"Easier maintenance" here often comes from the fact that different parts of an application can be developed more easily by different teams, so better distributed workload, specialized teams with clearer focus, and on.

Doc Brown
  • 199,015
  • 33
  • 367
  • 565
  • 5
    w.r.t. your last sentence, [Conway's law](https://en.wikipedia.org/wiki/Conway%27s_law) says that system structure tends to mimic org. structure: devs/teams are more familiar with some parts than others, so whilst fixes/improvements *should* happen in the most relevant part, it may be easier for a dev to hack it into "their" parts rather than (a) learn how that other part works or (b) work with someone more familiar with that part. This is related to the "seams" @TKK mentions, and how difficult it can be to find and enforce "correct"/simple ones. – Warbo Mar 13 '19 at 16:35
40

I'll have to disagree with the majority on this one. Splitting up an application into two separate ones does not in itself make the code any easier to maintain or reason about.

Separating code into two executables just changes the physical structure of the code, but that's not what is important. What decides how complex an application is, is how tightly coupled the different parts that make it up are. This is not a physical property, but a logical one.

You can have a monolithic application that has a clear separation of different concerns and simple interfaces. You can have a microservice architecture that relies on implementation details of other microservices and is tightly coupled with all others.

What is true is that the process of how to split up one large application into smaller ones, is very helpful when trying to establish clear interfaces and requirements for each part. In DDD speak that would be coming up with your bounded contexts. But whether you then create lots of tiny applications or one large one that has the same logical structure is more of a technical decision.

Voo
  • 765
  • 6
  • 11
  • But what if one takes a desktop application with multiple editing modes and instead just makes one desktop application for each mode that a user would open individually rather than having interfacing. Would that not eliminate a nontrivial amount of code dedicated to producing the "feature" of "user can switch between editing modes"? – user64742 Mar 13 '19 at 02:07
  • 3
    @TheGreatDuck That sounds like it would also eliminate a non-trivial amount of users who don't like having to switch between different applications. ;) But yes, eliminating features will generally lead to simpler code. Eliminate spell-checking and you will remove the possibility of having spell-checking bugs. It's just rarely done because the feature was added because someone wanted it. – Odalrick Mar 13 '19 at 10:24
  • 1
    @TheGreatDuck Surely the design of the UX should come before any architectural decisions. There's no point having the best designed architecture if nobody uses your program. First decide what you want to build and based on the decide on the technical details. If two separate applications is preferred, go for it. You can still share a lot of code via shared libraries though. – Voo Mar 14 '19 at 07:29
  • Is it really true to say the the complexity of _the system_ is due to the tight coupling of the parts? I would want to say that the total complexity _increase_ if you partition your system as you introduce indirection and communication, although the complexity of the _specific individual components_ are isolated in a bounded state of more limited complexity. – Alex Mar 14 '19 at 22:22
  • @Alex I think the confusion might stem from a misunderstanding what coupling means in this context. [The wiki article](https://en.wikipedia.org/wiki/Coupling_(computer_programming)) is not particularly great, but gives some idea. Low coupling basically means having modules that have clear responsibilities and bounds. High coupling leads to code where if you want to change one thing you'll have to fix code in lots of other parts, including other modules. – Voo Mar 15 '19 at 08:12
  • @Voo Sure, but is high complexity synonymous to high coupling? I'm thinking that complexity in one sense can be used just as a measure of the amount of components and connections, explicit or implicit, in a system. I'm thinking you use "complexity" as in "hard to manage"? – Alex Mar 15 '19 at 11:35
  • @Odalrick of course. I was merely countering the first paragraph’s line of reasoning by saying that depending on what one means by separate applications matters here. Going to a system using several processes forked off all at once is significantly different from just maintaining two products and is also different than one executable with one set of features. Understanding that there is a difference between all 3 certainly changes the answer one might give here. Of course I’m only looking at this from a maintainence standpoint. Changing an external interfaces is ***always*** bad marketing. – user64742 Mar 15 '19 at 23:36
  • Correction. It’s bad 95% of the time because obviously designs can improve. – user64742 Mar 15 '19 at 23:37
  • @Alex Yes complexity is a measure of the number of interactions between components (not just the number of components). The more interactions you have the more complex it is which is what really makes maintenance hard. You can't necessarily do much about how complicated your modules are (some problems are simply hard and can't be separated into easier problems), but your architecture has a big influence on how complex your system is. – Voo Mar 16 '19 at 11:30
  • 1
    @TheGreatDuck The underlying assumption here was that the systems have something in common and actually have to communicate in one way or another with each other. I don't think the OP was asking about whether two completely different applications that are bundled together for some weird reason would be easier to maintain if separated. Seems like a weird edge case that doesn't come up often in practice (although I'm sure someone somewhere has done that). – Voo Mar 16 '19 at 11:35
  • @Voo fair enough. I guess I had a different interpretation of the question then you. – user64742 Mar 26 '19 at 02:56
15

Easier to maintain once you've finished splitting them, yes. But splitting them is not always easy. Trying to split off a piece of a program into a reusable library reveals where the original developers failed to think about where the seams should be. If one part of the application is reaching deep into another part of the application, it can be difficult to fix. Ripping the seams forces you to define the internal APIs more clearly, and this is what ultimately makes the code base easier to maintain. Reusability and maintainability are both products of well defined seams.

StackOverthrow
  • 646
  • 3
  • 13
  • great post. i think a classic/canonical example of what you talk about is a GUI application. many times a GUI application is one program and the backend/frontend are tightly-coupled. as time goes by issues arise... like someone else needs to use the backend but can't because it is tied to the frontend. or the backend processing takes too long and bogs down the frontend. often the one big GUI application is split up into two programs: one is the frontend GUI and one is a backend. – Trevor Boyd Smith Mar 12 '19 at 17:32
14

It's important to remember that correlation is not causation.

Building a large monolith and then splitting it up into several small parts may or may not lead to a good design. (It can improve the design, but it isn't guaranteed to.)

But a good design often leads to a system being built as several small parts rather than a large monolith. (A monolith can be the best design, it's just much less likely to be.)

Why are small parts better? Because they're easier to reason about. And if it's easy to reason about correctness, you're more likely to get a correct result.

To quote C.A.R. Hoare:

There are two ways of constructing a software design: One way is to make it so simple that there are obviously no deficiencies, and the other way is to make it so complicated that there are no obvious deficiencies.

If that's the case, why would anyone build an unnecessarily complicated or monolithic solution? Hoare provides the answer in the very next sentence:

The first method is far more difficult.

And later in the same source (the 1980 Turing Award Lecture):

The price of reliability is the pursuit of the utmost simplicity. It is a price which the very rich find most hard to pay.

Daniel Pryden
  • 3,268
  • 1
  • 21
  • 21
6

This is not a question with a yes or no answer. The question is not just ease of maintenance, it is also a question efficient use of skills.

Generally, a well-written monolithic application is efficient. Inter-process and inter-device communication is not cheap. Breaking up a single process decreases efficiency. However, executing everything on a single processor can overload the processor and slow performance. This is the basic scalability issue. When the network enters the picture, the problem gets more complicated.

A well written monolithic application that can operate efficiently as a single process on a single server can be easy to maintain and keep free of defects, but still not be an efficient use of coding and architectural skills. The first step is to break the process into libraries that still execute as the same process, but are coded independently, following disciplines of cohesion and loose coupling. A good job at this level improves maintainability and seldom affects performance.

The next stage is to divide the monolith into separate processes. This is harder because you enter into tricky territory. It's easy to introduce race condition errors. The communication overhead increases and you must be careful of "chatty interfaces." The rewards are great because you break a scalability barrier, but the potential for defects also increases. Multi-process applications are easier to maintain on the module level, but the overall system is more complicated and harder to troubleshoot. Fixes can be devilishly complicated.

When the processes are distributed to separate servers or to a cloud style implementation, the problems get harder and the rewards greater. Scalability soars. (If you are considering a cloud implementation that does not yield scalability, think hard.) But the problems that enter at this stage can be incredibly difficult to identify and think through.

MarvW
  • 89
  • 2
5

No. it does not make it easier to maintain. If anything welcome to more problems.

Why?

  • The programs are not orthogonal they need to preserve each others work in so far as is reasonable, which implies a common understanding.
  • Much code of both programs are identical. Are you maintaining a common shared library, or maintaining two separate copies?
  • You now have two development teams. How are they communicating?
  • You now have two products that need:

    • a common UI style, interaction mechanisms, etc... So you now have design problems. (How are the dev teams communicating again?)
    • backward compatibility (can modeller v1 be imported into animator v3?)
    • cloud/network integration (if its a feature) now has to be updated across twice as many products.
  • You now have three consumer markets: Modellers, Animators and Modeller Animators

    • They will have conflicting priorities
    • They will have conflicting support needs
    • They will have conflicting usage styles
  • Do the Modeller Animators have to open two separate applications to work on the same file? Is there a third application with both functions, does one application load the functions of the other?
  • etc...

That being said smaller code bases equal easier to maintain at the application level, you're just not going to get a free lunch. This is the same problem at the heart of Micro-Service/Any-Modular-Architecture. Its not a panacea, maintenance difficulty at the application level is traded for maintenance difficulties at the orchestration level. Those issues are still issues, they just aren't in the code base any more, they will need to be either avoided, or solved.

If solving the problem at the orchestration level is simpler then solving it at each application level then it makes sense to split it into two code bases and deal with the orchestration issues.

Otherwise no, just do not do it, you would be better served by improving the internal modularity of the application itself. Push out sections of code into cohesive and easier to maintain libraries that the application acts as a plugin to. After all a monolith is just the orchestration layer of a library landscape.

Kain0_0
  • 15,888
  • 16
  • 37
4

There were a lot of good answers but since there is almost a dead split I'll throw my hat into the ring too.

In my experience as a software engineer, I have found this to not be a simple problem. It really depends on the size, scale, and purpose of the application. Older applications by virtue of the inertia required to change them, are generally monolithic as this was a common practice for a long time (Maya would qualify in this category). I assume you're talking about newer applications in general.

In small enough applications that are more-or-less single concern the overhead required to maintain many separate parts generally exceeds the utility of having the separation. If it can be maintained by one person, it can probably be made monolithic without causing too many problems. The exception to this rule is when you have many different parts (a frontend, backend, perhaps some data layers in between) that are conveniently separated (logically).

In very large even single concern applications splitting it up makes sense in my experience. You have the benefit of reducing a subset of the class of bugs possible in exchange for other (sometimes easier to solve) bugs. In general, you can also have teams of people working in isolation which improves productivity. Many applications these days however are split pretty finely, sometimes to their own detriment. I have also been on teams where the application was split across so many microservices unnecessarily that it introduced a lot of overhead when things stop talking to each other. Additionally, having to hold all of the knowledge of how each part talks to the other parts gets much harder with each successive split. There is a balance, and as you can tell by the answers here the way to do it isnt very clear, and there is really no standard in place.

CL40
  • 149
  • 1
  • 2
    My first job as a programmer was as a millenium-bug programmer. The software I was working on was split into hundreds of little programs which all did a little part, strung together with batch files and using files to communicate state. It was a big mess, invented in a time where computers were slow, had little memory and storage was expensive. When I worked with it, the code was already 10-15 years old. Once we were done they asked my advice and my advice was to convert everything to a new monolithic application. They did and a year later I got a big thank you. – Pieter B Mar 13 '19 at 17:26
  • @PieterB I have had a similar experience. "Cutting edge" tech is unfortunately a very large cargo cult in a lot of ways. Instead of choosing the best method for the job many companies will just follow whatever a FAANG is doing at the time without any question. – CL40 Mar 13 '19 at 17:33
  • and also: what may come out as a monolithic application once compiled, may be a very modular application, code wise. – Pieter B Mar 13 '19 at 18:09
2

For UI apps it is unlikely to decrease overall amount of bugs but will shift balance of bug mix toward problems caused by communication.

Speaking of user facing UI applications/sites - users are extremely non-patient and demand low response time. This makes any communication delays into bugs. As result one will trade potential decrease of bugs due to decreased complexity of a single component with very hard bugs and timing requirement of cross-process/cross-machine communication.

If units of the data the program deals with are large (i.e. images) then any cross-process delays would be longer and harder to eliminate - something like "apply transformation to 10mb image" will instantly gain +20mb of disk/network IO in addition to 2 conversion from in-memory format to serializabe format and back. There is really not much you can do to hide time needed to do so from the user.

Additionally any communication and especially disk IO is subject to AntiVirus/Firewall checks - this inevitably adds another layer of hard to reproduce bugs and even more delays.

Splitting monolithic "program" shines where communication delays are not critical or already unavoidable

  • parallelizable bulk processing of information where you can trade small extra delays for significant improvement of individual steps (sometimes eliminating need for custom components by using off-the-shelf once). Small individual step footprint may let you use multiple cheaper machines instead of single expensive one for example.
  • splitting monolithic services into less coupled micro-services - calling several services in parallel instead of one most likely will not add extra delays (may even decrease overall time if each individual one is faster and there are no dependencies)
  • moving out operations that users expect to take long time - rendering complicated 3d scene/movie, computing complex metrics about data,...
  • all sorts of "auto-complete", "spell-check", and other optional aids can and often made to be external - most obvious example is browser's url auto-suggestions where your input send to external service (search engine) all the time.

Note that this applies to desktop apps as well as web sites - user facing portion of the program tends to be "monolithic" - all user interaction code tied to single piece of data is usually running in a single process (it is not unusual to split processes on per-piece-of-data basis like HTML page or an image but it is orthogonal to this question). Even for most basic site with user input you'll see validation logic running on the client side even if making it server side would be more modular and reduce complexity/code duplication.

Alexei Levenkov
  • 312
  • 1
  • 10
1

Does [it] help prevent bugs?

Prevent? Well, no, not really.

  • It helps detect bugs.
    Namely all the bugs you didn't even know you had, that you only discovered when you tried to split that whole mess into smaller parts. So, in a way, it prevented those bugs from making their appearance in production — but the bugs were already there.
  • It helps reduce the impact of bugs.
    Bugs in monolithic applications have the potential to bring down the whole system and keep the user from interacting with your application at all. If you split that application into components, most bugs will —by design— only affect one of the components.
  • It creates a scenario for new bugs.
    If you want to keep the user experience the same, you will need to include new logic for all those components to communicate (via REST services, via OS system calls, what have you) so they can interact seamlessly from the user's POV.
    As a simple example: your monolithic app let users create a model and animate it without leaving the app. You split the app in two components: modeling and animation. Now your users have to export the modeling app's model to a file, then find the file and then open it with the animation app... Let's face it, some users are not gonna like that, so you have to include new logic for the modeling app to export the file and automatically launch the animation app and make it open the file. And this new logic, as simple as it may be, can have a number of bugs regarding data serialization, file access and permissions, users changing the installation path of the apps, etc.
  • It is the perfect excuse to apply much needed refactoring.
    When you decide to split a monolithic app into smaller components, you (hopefully) do so with a lot more knowledge and experience about the system than when it was first designed, and thanks to that you can apply a number of refactors to make the code cleaner, simpler, more efficient, more resilient, more secure. And this refactoring can, in a way, help prevent bugs. Of course, you could also apply the same refactoring to the monolithic app to prevent the same bugs, but you don't because it's so monolithic that you're afraid of touching something in the UI and breaking business logic ¯\_(ツ)_/¯

So I wouldn't say you're preventing bugs just by breaking a monolithic app into smaller components, but you're indeed making it easier to reach a point in which bugs can be more easily prevented.

walen
  • 345
  • 2
  • 9