DSL's, libraries and services

Languages in the Lisp family are famous for allowing the programmer to create DSL's (Domain Specific Languages) of sorts. In the process of writing a program or library, you may create a small DSL to make your program more readable.

Except it isn't really more readable is it. You just created another small language that someone else has to learn. Chances are, if you come back to your code in 6 months, you will also have to learn it again.

This is similar to writing libraries: you abstract your code away behind an interface, but this interface is something that must be learned, and is something that can be forgotten easily. You must internalize the library interface - learn it - before you can use it effectively. This takes time and effort, and if that time and effort is only going to be done once by one person then it's wasted time.

The Java language more or less forces you to abstract things away behind intefaces. So your program becomes a collection of custom interfaces (kinda like a DSL of sorts) that you have to load into your brain before you understand the program. This is seen as a positive thing: encapsulation, data hiding, abstraction, etc.

Don't be afraid of using the languages builtins directly and obviously. Don't obsess over hiding things away, thinking you are doing someone else a favor. When you hide code away behind an abstraction, chances are someone is going to have to read that code anyway to understand how to use your abstraction.

I see Service Oriented Architecture in the same way. Unless your service has a really really obvious interface, someone using your service is going to have to understand how your service works internally. You might argue a user of your service only needs to understand some basic high level concepts in order to use your service (which you describe clearly in your documentation, right?) but even then it is not enough. Because your service will have strange bugs related to the impelmentation, and your going to end up explaining those bugs to your users. Unless of course you write perfect code, and your service is fully functional from day 1 with no bugs...

We create services, libraries and frameworks for a different reason: when we want to reduce the functionality of a certain domain for reasons ouside of programming. A company might create an internal web framework, not because they want to make it easier to write web applications, but because they want to enforce restrictions on other developers so that the company applications have a consistent look and feel. Or we might create a backend web framework because we want to enforce a particular way of doing logging, or a particular way of doing service discovery. We sell that to users with a promise of simplicity, but it's not really. Documentation is never comprehensive, frameworks and services are littered with bugs - this introduces complexity. We create these restrictions so we can have all logs go to a unified place, or so we can have all applications looking the same to not confuse employees with a rainbow of application styles.

And this is not a bad thing. These are not bad reasons for introducing these things. Just never let yourself be fooled into believing that it will make your life, as a user of these frameworks or a consumer of these services, easier.

All you can do is to try as hard as you can to do the following things:

  • If you build a service or a library, make sure your documentation describes the inner workings to some degree. For example, what records are stored in what tables internally, and the various states these records can get into given various external actions. You don't need to describe column types or anything like that, but at a slightly higher level - what are the states maintained by your service or library, and how do external actions modify those states. This is not the same as an API contract, and simply publishing your API with swagger doesn't count.
  • When writing code to be worked on by your teammates or fellow contributors, prefer direct and obvious implementations. Don't try to build some internal framework that only your team uses. Don't try to hide code from your teammates, because it's not going to work - they are going to have to debug your hidden code at some point.