There has been a rise of solutions for application development that identify themselves as ‘low-code platforms’. On the websites of these vendors you can read a lot about low-code development but it still might confuse you a bit. I would like to take a more neutral point of view by dissecting the concept of ‘low-code’ from a (historic) software design perspective. What is it? Is there also such a thing as ‘high-code’? And when should I go with ‘low-code platforms’ anyway?
Low and high code
Low-code simply means that few lines of code are required in order to develop a certain application. More generalized it means your specification for your application is relatively small. Reducing the lines of code is a worthy endeavour for any development of software. Science suggests that fewer lines of codes leads to fewer bugs and, well, who doesn’t want that? Fewer lines means the code will be easier to read and to comprehend. Furthermore, removing lines of code whilst maintaining the same level of functionality usually leads to a more succinct code base where intent is better notated and specified. These qualities, in turn, foster better communication between developers and bugs are easier to spot just by reviewing the code. What is not to like?
Indeed, everything is to like. And that is why there is a long history in programming of chasing low-code. But first, we must talk about it’s logical counterpart: ‘high-code’. Never heard of it? Me neither. It is not an actual concept in programming or something that you read about, but there can’t be good without bad, right? Because low-code can only be low if compared to something… high. But what is it being compared with? C++ code? JS Code? Assembly? The answer is that there is no definitive ‘high-code’ solution, and also not a definitive ‘low-code’ solution. It is all relative. Shades of grey so to speak. Therefore, referring to low-code, or high-code, makes little sense if you don’t know what you are comparing it with and what you want to build.
If we allow ourselves to take this relativistic view and accept that there are many layers of high-code and low-code, we will see that the search for low-code goes back to the early days of programming. There are two main constructs essential to low-code: abstractions and code reuse.
Abstractions and code reuse
The earliest programmers quickly got tired of carving out machine code so they promptly invented assembly which allowed a symbolic and readable approach to programming. Although it made programming considerable easier, it did not directly lead to low-code as there was almost a 1:1 mapping between machine instructions and assembly operations. The first big leap into low-code came with the first ‘high-level’ programming languages that supported structured programming such as ALGOL and C. In these languages, common patterns such as for loops and nested branching could explicitly be specified. Something we take for granted nowadays, but what a revelation it was! Further research in programming languages gave us ever more abstractions such as functions, modules, generics and classes. These abstractions allowed programmers to specify their programs in an increasingly more concise way. Unfortunately, most big leaps in abstractions, the low-hanging fruit, were discovered early on so this seems to be a case of diminishing returns. That is why old languages such as C are still popular and relevant; the abstractions offered by these languages are deemed good-enough for many applications. We are not done yet, though. In the last decade efforts have been largely on designing better abstractions to deal with asynchronous and parallel programming in response to the realities of distributed systems and the rise of CPU’s with multiple cores. So, coming up with better abstractions to allow more expressive programs in fewer lines of code is nothing new and has been an ongoing research effort for decades.
The second construct that leads to low-code is by reusing code through the use of libraries or frameworks. As programmers, we all stand on the shoulders of giants. Giant libraries, that is. The amount of code that is executed for even the simplest of web applications is staggering, but nowadays a junior programmer can quickly create a minimal Node.js web app in just a handful of lines just by reusing the code in libraries. Most programming languages include a standard library that contains code for most common data structures and algorithms. Besides a standard library, many software ecosystems, or platforms, also have standardized (or endorsed) libraries for dealing with files, sockets, threading, etc. The .NET framework alone contains thousands of classes and millions of lines of code covering a very broad range of functionality. Code reuse through standard libraries and frameworks are a huge production boost for all types of application development. It leads to a massive decrease in the required lines of code, and thus to low-code.
So now we use abstractions and libraries to achieve our goal of low-code. You might be wondering: how low we can go? The boring answer is: it depends. Obviously not all applications are equal. And not all can be written in a single line of code, so there must be forces holding you back from writing fewer and fewer lines of code. I’d like to view the lower bound as being set by something called essential complexity. That is: how can we specify (code) our application as concise as possible whilst still implementing the required functionality. Essential complexity is a very important and useful concept, but how can we know what is essential and what is not? It requires quite a bit of experience to recognize the difference but I can tell you about two ‘forces’ that will inevitably increase essential complexity and therefore will make you write more code.
Firstly, the novelty of your application with respect to functionality correlates with greater essential complexity. That is, applications with truely novel features tends to lead to more lines of code because, well, nobody has written the required code before. You are on the frontier, what a joy! So the first guy or woman implementing a certain cryptographic protocol is forced to write much more novel code than all following programmers who are just using a library, such as OpenSSL, where that functionality is already ready to use. The same can be said about the first chess A.I., deep-learning algorithm or blockchain application. If you are developing cutting-edge functionality (lucky you!), there simply is no code to reuse and you have to write it out on your own. In contrast, if you are developing a common CRUD enterprise application then there are many frameworks that all you to deliver this application with little code. Indeed, this is what most low-code platforms can offer you.
Secondly, the breadth of required functionality impacts essential complexity and thus forces you to write less or more code. If the client dictates that your application must record not only the name of customer but also it’s e-mail adress then you must specify this fact in, amongst others, the database schema, API schema and validation code. And even if this code is generated somehow from a higher-level artefact, you must still specify it at least once. There is simply no way around it.
The opposite of essential complexity is incidental complexity. This encompasses code that not really specifies any essentials but rather is there to satisfy the programming language or other parts of the system. This includes typical boiler-plate code that is seem in move verbose languages such as Java. You want as little incidental complexity as possible because it takes time to write, it can contain bugs and makes your code harder to read. Incidental complexity, or boiler-plate code, is a force pushing back low-code towards high-code.
It is important to note that when we speak of ‘code’ we must include all artefacts required to build an application. This includes code files, build files, build instructions, documentation, code generators, database schemas, API specifications. If we don’t include everything it is easy to move around complexity from one artefact to another and falsely claiming we have reduced complexity. For example, a build file can be simple if the complexity of building the system is moved to a written manual containing complicated building instructions. Having a simple build file rather than a complex one is not low-code development if you have simply moved complexity elsewhere.
So we now know what low-code is and how we can achieve it. How can it help in evaluating whether self-proclaimed low-code platforms are useful? Here are some aspects to think about.
In my experience ‘low-code’ does not mean that inexperienced people can develop applications, even though some people may tell you otherwise. This is because some experience in software design is still required, even in low-code environments. If one has no knowledge of good design the application will still lead to an unmaintainable mess in the end. As an aside: if somebody is actually experienced and has the skills to develop an application with good design than I would call that person a developer, regardless of the actual job title. Although low-code platforms are arguably easier to develop on, it still requires experience, skill and dedication to create maintable and effective applications.
Sometimes ‘low-code’ means coding using visual programming tools such as diagrams and workflows. In my experience, visual programming is almost never a good approach but it tends to work okay-ish for specific things such as GUI interfaces and database table schema’s. But visuals are terrible for expressing logic. As a consequence of this limited expressiveness the applications that can be developed on such platforms are either constrained in scope or there is a backdoor that stil allows for code which kinda complicates the whole thing and suddenly you’ll find yourself in need of a programmer or reading coding tutorials. But you’ll only find out later unfortunately. MS Access is a good example of a ‘low-code’ platform that uses visual programming for database schema’s and interfaces, but it resorts to Visual Basic code for expressing logic. And as many know from experience, MS Access can be very powerful for the right job but an absolute monster if used wrongly*.
Often ‘low-code’ platforms provide frameworks and libraries (sometimes called apps). This allows for significant code reuse, BUT it usually also constrains the scope to a certain kind of application. Do not expect to create video games using your typical low-code platform for enterprisy database applications! Note that using a framework or library is very common in all software ecosystems and is not a distinguishing feature of low-code platforms at all. For example, Ruby on Rails can also be considered a ‘low-code’ platform that allows for rapid application development using it’s ‘convention over configuration’ approach.
Many ‘low-code’ platforms support an integrated application life cycle. This integrated approach enables low-code by reducing the number of artefacts required for building and deployment. Maybe even more important, it enabled a company-wide standardization of release management, source control and package management. This allows for a more centralized and controlled development process. Especially in enterprise development this is a huge advantage that cannot be underestimated. It reduces ‘hidden it’ and leads to better governance and reduced risks. In fact, I believe this to be the main advantage of low-code platforms. Platforms such as Microsoft’s Azure Service Fabric also offer such functionality but it is more generic and ‘low-level’ than most low-code platforms.
As we have seen, low-code is not a novel idea and a lot of software applications in history can be considered ‘low-code’ platforms if you take into account the context of the application and the historical point in time. Older development tools such as MS Access, Oracle Forms, Visual FoxPro and Dreamweaver are, or have been, ‘low-code’, although it might not be obvious. As history shows: using software and tools that bring better abstractions and more reuse of code to the table will lead to low-code.
More modern low-code platforms have innovated on their ease of delivering web applications instead of desktop applications, which is a crucial difference compare to older alternatives. By offering the right abstractions and frameworks it allows even junior developers to create powerful online applications. As long as it doesn’t deviate too much from the intented scope. And, just as important, they often support integrated application lifecycle management that allows distributed development. That is something that older platforms do not offer and is of much value for modern enterprises.
If your application fits well within the intended scope of the platform then it might offer you a very value, especially within larger enterprises. If, however, the functionally that you seek in your application is not well supported by the platform, rest assured that these platforms are not the pinnacle of software development and that there are many more ways to build applications using ‘low-code’.
* During an internship I once had to write a personnel planning system in MS Access but I was not allowed to use VB code because they feared it would not be maintainable. As a young man eager to prove that it could still be done, I ended up with a giant cluster of tables, views and queries to implement a simple heuristic to the knapsack problem. It would have been a lot simpler if just a bit of code would have been allowed. But it worked. My internship just lasted a couple of months and sometimes I still think back of the pains I must have put my successor in … A lesson well learned, though.