Green Software Engineering — Back to the roots!

Thilo Hermann
10 min readFeb 17, 2022

Sustainability is one of the hottest topics in IT currently. It’s quite obvious that also Software Engineering has an impact on our environment. It might not as big as optimizing the steel industry, but still there is a value in having a closer look.

Green Software Engineering is an emerging discipline with principles, and competencies to define, develop, and run sustainable software applications. The result of Green Software Engineering will be a Green Application.

Green applications are typically cheaper to run, more performant, and more optimized — but that’s just a welcome addition and I will explain the reason for this correlation later. The key thing is that developing applications in such a manner will have a positive impact on the planet. So, let’s have a closer look at the principles. Please note that Green Software Engineering is just one part of sustainability in IT, but in this blog I will focus on it!

According to https://principles.green/ the following principles are essential when building green applications:

  1. Carbon: Build applications that are carbon efficient.
  2. Electricity: Build applications that are energy efficient.
  3. Carbon Intensity: Consume electricity with the lowest carbon intensity.
  4. Embodied Carbon: Build applications that are hardware efficient.
  5. Energy Proportionality: Maximize the energy efficiency of hardware.
  6. Networking: Reduce the amount of data and distance it must travel across the network.
  7. Demand Shaping: Build carbon-aware applications.
  8. Measurement & Optimization: Focus on step-by-step optimizations that increase the overall carbon efficiency.

You should take code and architectural changes into account that reduce the carbon emissions and energy consumption produced by your application. Please note that most of the examples are based on Java and Cloud Technologies like Containers.

Just another NFR?!

When I read these principles, it came to my mind that those should be reflected in non-functional requirements for the application. If you treat them like this, it’s obvious that you must find the right balance and typically there is a price tag attached.

The good news is that green principles are also related to well-known non-functional requirements like “Performance Efficiency” (see ISO 25010 https://iso25000.com/index.php/en/iso-25000-standards/iso-25010 ).

Those NFRs regarding Performance and Efficiency can typically be fulfilled by optimizing your code. We often have challenging performance requirements and once you optimize your algorithms (e.g. moving from a “bubblesort” with the complexity of O(n²) to a “quicksort” with the complexity of O(n*log(n)) ) will reduce the CPU utilizing especially for huge data sets. Those optimizations also have a positive effect on energy consumptions and thus for the CO2 emissions.

On the other hand, a brute force solution by keeping the in-efficient algorithms and just adding additional hardware (e.g. CPU, RAM) might work for the performance NFR, but not for Efficiency and thus this lazy approach will have a negative impact on your sustainability targets! Especially in the cloud with “unlimited” scaling this solution could be tempting for developers.

You might remember your computer science lectures around “Complexity Theory” and especially about “Big O Notation” and you might have wondered what those are good for … now you know that those are key for a sustainable world! A Green Software Engineer has to be a master in implementing highly efficient algorithms!

You should be aware that the NFRs are sometimes conflicting, and you must make compromises. It’s a best practice to document your Design Decisions and their impact on NFRs. With this it’s easy to visualize the impact and conflicts. Once you know those it’s the right time to take decisions! One question still to be answered: When and how to optimize your green application?

Let’s start with two quotes from pioneers of computer science:

First rule of optimization: Don’t do it.
Second Rule of Program Optimization (for experts only!): Don’t do it yet — that is, not until you have a perfectly clear and unoptimized solution. — Michael Jackson

Programmers waste enormous amounts of time thinking about, or worrying about, the speed of noncritical parts of their programs, and these attempts at efficiency actually have a strong negative impact when debugging and maintenance are considered. We should forget about small efficiencies, say about 97% of the time: premature optimization is the root of all evil. Yet we should not pass up our opportunities in that critical 3%. — Donald Knuth

To rephrase it: “Know your enemy” and optimize according to the following algorithm.

  1. define your target
  2. measure as accurate as possible
  3. identify the bottle necks
  4. optimize the biggest bottle neck (and only one at a time!)
  5. measure again
  6. check if you reached your target?
    a) Yes → you’re done.
    b) No → go to step 2.

With this approach you should be able to avoid micro-optimization and also premature optimization which is the “Root of All Evil” (see https://stackify.com/premature-optimization-evil/).

To move on: It’s not only the next NFR, but rather a mindset change to strive for resource efficiency wherever appropriate.

Abstraction as environment killer?

Since I started my career in computer science the level of abstraction grew over time. In the beginning I was still learning machine/assembly language (for the famous 6502 microprocessor), moved on to C/C++ and later to Java and finally to Low Code (e.g. Mendix, Outsystem, Microsoft PowerApps). This made live as programmer much easier and efficient, but the drawback is that with all those abstractions the actual resource usage is hidden and typically went up.

The obvious conclusion of this is that we should move back to machine language to build highly efficient green Applications. “Unfortunately”, this is an aberration for several reasons:

  • It’s really tough and time consuming to implement in machine/assembly language
  • Complex, huge systems are tricky to implement even with a higher level of abstraction
  • Cost most probably will explode
  • Time2Market will be much longer

On the other hand, compilers have improved a lot in the last years and they often optimize the code better than an average programmer is able to do. Techniques like Loop optimization, Inline expansion, Dead store elimination to name a few will improve the efficiency and thus lead to a greener application.

In some cases, it might be worth choosing a lower level of abstraction (e.g. use C) to optimize to the max, but this has to be an explicit decision for the really heavily used code parts. As already shown above you need to “Know your enemy” and only optimize according to this pattern where you expect a huge impact!

Complex Frameworks/COTS vs. Lightweight Alternatives

Besides abstraction the usage of frameworks and … has an impact on the green features of your application. Middleware COTS products like application servers (e.g. IBM Websphere, Oracle WebLogic, JBoss, …) introduce complexity which might not be needed in each and every case. Lightweight alternatives like Tomcat or even further Spring Boot (see https://spring.io/projects/spring-boot), Micronaut (see https://micronaut.io/) or Quarkus (see https://quarkus.io/) will reduce the memory footprint, startup time and the CPU usage a lot. So once more you should check, if you really need the complex and feature rich frameworks or if the lightweight alternatives are good enough. For most of the cases I was involved the lightweight alternatives are perfectly fine!

I strongly recommend selecting the “lightest” framework that still fits to the requirements. Don’t stick to given or even mandatory standards and be willing to fight for a greener alternative!

Architecture — Microservices vs. Monoliths

Architecture can have a huge impact. Over the years architectural pattern evolved and currently Microservices are an often-used pattern. Once you compare legacy monoliths with a modern microservice architecture there are positive and negative effects on the resource usage:

Positive:

  • Scaling: With a Microservice Architecture it’s easy possible to scale only where needed. Mechanisms like auto-scaling on container platforms (e.g. Kubernetes, OpenShift, AWS EKS, MS Azure AKS) do this only on demand and thus reduce the overall resource usage.
  • Best Technology: You can choose the best fitting technology (e.g. Databases, Programming Languages) for your purpose and thus reducing the complexity and level of abstraction for every Microservice independently. This will lead to a more efficient application, if done in a proper manner.

Negative:

  • Network Traffic: Within a Microservice Architecture the number of “external” calls is much higher than in a monolithic application. Even with lightweight protocols this introduces an overhead and thus the resource usage will go up. If there is an API Management tool involved the overhead is even bigger.
  • Data Replication: It’s quite common to replicate data in a Microservice Architecture to enable independence between the services. This will lead to a higher storage demand and the propagating of changes via events (e.g. Event Sourcing, Command Query Responsibility Segregation (CQRS)) will increase the network traffic and CPU utilization.

You need a case by case evaluation, if the chosen architecture has a positive effect on the green principles or not!

… and now to something completely different: No blog without referring to the KISS principle (see https://en.wikipedia.org/wiki/KISS_principle).

KISS — Reduce to the max

For all green software developers, the KISS principle shall be applied on the following dimensions: CPU — RAM — DISK — NETWORK

Now let’s have a look on what are typical measures to achieve those reductions:

  • Efficient Algorithms: That’s obvious and already explained in the beginning of this blog. The better the algorithm works (e.g. CPU utilization, Memory Consumption), the faster you will reach your target. The reuse of existing libraries which include optimized solutions for common problems is a best practice.
  • Efficient Data Structures: Only persist data that is really needed to be stored. As storage is available especially in the cloud almost unlimited, we tend to just use it. Instead of persisting it as it is and sometimes even more than needed, we should rather only persist what we need later. For example, derived values are not necessarily needed to be persisted. Please note that it’s always a trade of especially for queries on the database.
  • Caching: Caches can reduce the amount of external service calls to a minimum. This includes database, file systems, external services, web-content calls. Be aware that when introducing caches, you must make sure that the functional requirements are still fulfilled. You might get eventual consistency as drawback.
  • Compression: Data compression can be applied on several dimensions. Communication protocols shall be optimized in respect of size. For example, moving from SOAP to REST already will reduce the size and the marshaling and un-marshaling effort. You can even go further and use binary formats (e.g. like gRCP, ActiveJ). The drawback of binary protocols is that they are not easily human readable and some lack interoperability with other programming languages. Besides protocols you should also check if you can reduce the resolution of graphics used within your (web) application, or even better move to textual representations. Tree-Shaking is another approach which is often used in JavaScript to reduce the amount of code transferred/compiled in the web browser.
    In general, if you compress and decompress during runtime you need to calculate this overhead and check if it’s a real efficiency improvement.
  • Scream Tests: The concept of Scream Test is quite simple — remove the application/service and wait for the screams. If someone screams, put it back online. This helps to get rid of unused applications/services. If you are lucky no one screams and thus this will reduce resource consumption.

Recurring fully Automated Tests

With the rise of Automated Testing in CI/CD Pipelines as standard for engagements the electricity consumption went up. If you take DevOps seriously you need to have highly automated tests with a high test coverage which are executed frequently. This should include functional and non-functional tests.

Should we get rid of those tests for the sake of sustainability?

For sure not, but we might need to optimize our testing strategy. Instead of running all tests we should rather focus on running the relevant ones. Thus, you need to know the dependencies and the impact of the changes you did. The exiting tools support you in this, so you should avoid the “brute-force” approach of testing everything after every build.

Maintenance and Monitoring

As software is modified and maintained it’s important to keep an eye on the side-effects of changes. A simple change on a well working algorithm might have a major impact on the energy consumption. On the other hand, a software might not scale properly. Over time you might have more users and/or data. This can have an impact on your carbon footprint especially if your code is not “behaving” well. This is already explained above with the sort algorithm example. Just to make it tangible:

It’s obvious that for n=10 all algorithms can work properly but scaling n to 1000 will impose challenges for some of them.

Measurement is important to identify those changes. If you implemented a well-working monitoring within your application, you’re able to identify such things during regular operations. You must watch out for trends to act early instead of reacting later. So “Green Software Engineering” is not a one shot but rather an ongoing activity.

Timing

Renewable energy like wind power and solar energy produces less carbon than traditional approaches. Unfortunately, sometimes the sun is not shining, or the wind is not blowing, during those times you need other sources like coal or gas. There are statistics available that clearly show this effect. If you’re able to schedule your workloads, e.g. for batches you can benefit from this by running those programs during a time period with a lower overall carbon footprint.

… and now for something completely different — AI?!

Machine Learning and AI are an interesting topic. The training of a neural network model can emit as much carbon as five cars in their lifetimes. And the amount of computational power required to run large AI training models has been increasing exponentially in the last years, with a 3.4-month doubling time (see https://hbr.org/2020/09/how-green-is-your-software). On the other hand, you might save a lot of carbon and/or electricity by using those models. It’s somehow an invest and one needs to calculate a “business case” to take the right decision. This seems to fall in the well-known “it depends” category of the consulting business.

It’s obvious that we need optimized algorithms in the future in this area (i.e. training of neural networks and also using them) and the research on this is at its starting point.

Know the basics, learn from the past …

Green Software Engineering also reminds me on the typical waves we have in IT (as already described in https://thilo-hermann.medium.com/good-architects-are-like-surfers-aa49e516abe2?source=friends_link&sk=0c939b319d78a07f249c0bb428a66b59) and adds another wave: Efficient Application vs. Efficient Development.

Finally, I would state the following: Every experienced software engineer with a strong focus on performance and efficiency is well equipped to become a Green Software Engineer!

And sometimes the best solution for the environment would be to get rid of the application completely to save the planet! For whatever reason Bitcoin comes to my mind while I’m writing this ;-)

--

--

Thilo Hermann

Thilo has more than 25 years of experience in IT Architecture and worked for several clients in Germany. He’s located in Stuttgart and works at Capgemini.