How To Tell Whether Your Web Framework Is Shit

Does it ship with unit tests?

If it doesn’t, it’s shit. Before basing the future of your business (or even your hobby) on a Web framework, see if you can find the tests. If you can’t, either move on, or get ready to take on someone else’s terrible, smelly code. Whoever wrote it doesn’t care about software quality. They are content to “code now, debug later”. Ultimately, you will pay for it:

  • The methods will look like shit. They will be too complex, and hard to maintain. They will be horribly long, and have a high cyclomatic complexity. They will get involved with other methods’ business.
  • The classes will look like shit. They will be too long. They will have methods that rightly belong in other classes. They will be overly involved in other classes’ business.
  • The overall design will be a gigantic, jumbled mess. It will basically be a lava flow. You will find anti-patterns everywhere.

How’s the code coverage?

If you run code coverage on the framework, and it’s less than 90%, you are looking at a huuuuuge pile of crap. Even with totally half-assed TDD, you will reach 90% without even trying. (I would know. I used to be that guy. Now I’m this guy. This guy knows how much easier life gets with TDD.)

If the code coverage is high, but you run it and there’s 10 tests and 15 assertions to cover 1,500 lines of code, you’re really just looking at an integration test. That’s vastly better than most of what you’ll find out there. It isn’t really good, though.

If the framework’s code coverage is 100%, and the tests are good or mostly good, you’re in luck.

Does it encourage you to write monolithic code? (If so, can you do anything about it?)

If your framework demands that you place all of your business logic (e.g. Controllers) and business rules (e.g. Models) under one subdirectory that is provided by the framework itself, you are looking at a de-facto monolithic framework. An example would be Laravel/Lumen’s “app” directory.

DON’T do it. Don’t.

Oh, sure, it’s easy. You’ll start by writing one Controller, and a few models. Then, you’ll write another Controller, and another few models.

Six months later, you have twelve Controllers, and fifteen or twenty Models.

How do you know the dependencies between these things? How do you know what will break if you change something?

If you wanted to deploy arbitrary combinations of back-end services (rather than the entire thing), could you do it without performing major surgery on your code?

Fortunately, many modern Web frameworks allow you to install “libraries” (or some similar concept) using dependency managers. For example, PHP has a horrendous and sometimes functional one called Composer. Other languages have their own package managers. If you can find a way to force the framework to load your business logic from libraries (or whatever your language calls them), and those libraries live in their own repositories and can be installed from the dependency manager, then you may just be in luck.

It is possible, and very easy, to override the default behavior in Laravel and Lumen, and to bootstrap your code to run out of Composer libraries. Each lives in its own Git repo, so you can deploy them in whatever combination you like. The Laravel/Lumen docs will not tell you how, but if you spend even half an hour looking into it, you can figure it out. You can actually cause Lumen to be the microservice framework it claims to be, rather than the baby monolith framework it actually is.

How well does it comply with the SOLID design principles, if at all?

If you have even cursory experience with SOLID, you know why the standard “program whatever I want, everything is a lava flow, big ball of mud, nightmare to maintain” methodology is really, really bad.

When you look at most of the Illuminate libraries (which underpin Laravel/Lumen), you will see God objects, spaghetti code, and general bad design in many places. (Oh yeah, they don’t ship unit tests either. That’s the code you want to run your enterprise on?)

Does it force you to use HTTP, a message queue, or some other communication method? Does it force (or over-assertively “encourage”) you to speak JSON, or some other format?

If you look at Laravel, everything is about HTTP. Everything seems to depend on HTTP. There is also a lot of magic stuff in the background that assumes you are speaking JSON.

Remember when you barely knew anything about programming, and you first encountered the “tight coupling vs. loose coupling” debate? Well, here’s the deal. You might not think it’s necessary, but if you’re doing anything beyond a hobby (or if it’s just a hobby, and you want to beef up your skills for a career move), you have to assume the following:

  • You will find some reason to use a pipe other than HTTP. You’ll find something that’s faster, and need it, in order to keep your page load times down and your conversions up.
  • You will find some reason to use a format other than JSON. It’s good, but it’s not the best. There are tighter and more binary-safe formats out there. They transfer data quicker. Again, think about your page load times, if nothing else.

If your framework demands HTTP or a message queue, and you want to try something else, you will have to change it in, like, eighty goddamn places. Likewise, if you want to use some serialization format that isn’t JSON, you will have to change it in, like, eighty goddamn places.

The microservice movement adopted the “smart endpoints, dumb pipes” methodology because when the pipes are too smart, they begin to outsmart you. The abject nightmare called ESB, where the “bus” was roamed by middleware that screwed with your messages and stole responsibility for your services’ interfaces and behavior from you, is exactly why that movement started.

ESB is COBOL. It just doesn’t know it yet.

Your business rules and logic should be completely abstracted from the following:

  • Communication channels (HTTPS, queues, and other things that transmit data)
  • Communication formats (HTTP Request/Response, JSON, SOAP, etc)
  • Deployment details (several services in one instance, services talking to other services using some communication channel and format, services running in Docker containers, services running in “serverless” things like AWS Lambda, etc)

You should be able to delay decisions on communication channels and formats, and deployment details, until the very last responsible moment. The only way to do so is to abstract your business logic and rules completely from such details. They should have no idea what’s going on behind the curtain. When that’s the case, what’s behind the curtain becomes irrelevant to them, and can be swapped out at any time.

If you want to change communication pipes or formats, you can change them in one place.

If your cloud provider comes up with some fancy new environment (like Lambda), your code will not care at all. You may have to do some minor work on the underlying framework, but you can move quickly without having to change a single line of your business logic or rules.

You should expect this to happen. Amazon, Microsoft, and (presumably) Google are always looking for some new thing to do with their cloud services.

Further Reading

Look up Robert C. Martin (“Uncle Bob”) and Martin Fowler.

Fowler is totally wrong about code coverage, but right about most other things. He literally wrote the book on refactoring. (It’s called Refactoring.)

Uncle Bob is pretty much right about everything, all the time. He published the SOLID Design Principles, Clean Code, Clean Architecture, and many other things. He is my primary source. Many of the ideas in this post came from his work.

Both of these people have spent many decades making every mistake under the sun so that you don’t have to.

Published by PDQ

Programming since the '80s.