Wednesday, August 23, 2023

Green-bar-shippable software

I can't remember where exactly I heard about the concept of "green-bar-shippable software", maybe it was in a conference talk or in some book, but somehow the Internet forgot and doesn't bring up anything useful. Lets rectify that and note it down for posterity's sake!

As the name suggests, green-bar-shippable software is software that can be shipped when running the test suite presents a "green bar" indicating all tests have succeeded.

Put differently, geen-bar-shippable software is software where the developers have such a high level of confidence in their automated tests that they consider a successful build sufficient sign off to release the software to customers.

I'm a big fan of this principle and I think it's the gold standard to strive for. Every bug that testers find or is discovered in a production deployment, essentially highlights a shortcoming in the test suite. Test driven development of course also leads in this direction.

Saturday, February 13, 2021

HTML over the wire

Yes please!

DHH is on to something with his recent post on HTML over the wire. It's high time that we start tearing down this gigantic mount of complexity that we've built the last two decades and get back to application development that developers can actually understand and excell with!

Friday, June 12, 2020

YAML vs XML

Remember how everybody used to complain about "programming in XML", for instance in Spring bean definition files? Luckily we've moved beyond that and are now in the brave new world of "programming in YAML", where we can spend (waste) hours in the cloud writing Kubernetes YAML manifests, Terraform YAML templates, and so on.
  • Even less readable!
  • No more syntax checks with proper schemas!
  • Go crazy fixing stupid indentation mistakes!
  • ...
Oh the joys! Sigh...

Thursday, January 30, 2020

The Modular Monolith

Having been a micro-services sceptic from the start, and a proponent of the Monolith First approach, I've found Kamil Grzybek's Modular Monolith article series quite insightful:

Starting with a well designed and internally modularised monolith does seem to be the way to go. When architectural drivers change, like different modules evolving at different rates, it comes natural to split up the monolith into a few modules that are life-cycled and deployed independently, essentially creating micro-services. Scalability concerns in a module is another example of an architectural driver that often leads to such a split up.

You've got several tools at your disposal to keep your monolith properly modularised.

  • Designing proper module interfaces used by other parts of the system to interact with the module.
  • Use of Java packages to encapsulate modules, while keeping an eye on cyclomatic complexity.
  • The Java 9 module system. (Although I haven't used this myself yet.)
  • Smart use of multi-module builds in Maven. If you get this right, extracting a module to become its own micro-service might be a relatively trivial matter.
My basic advice would be: focussing on creating properly designed, modularised and high quality software first will make sure micro-services come easy later on.

Friday, August 2, 2019

Adding a JDBC driver to Spring Cloud Data Flow server

A quick tech note on how you can add a JDBC driver dependency (jar) to the classpath when launching the spring-cloud-dataflow-server.

The Spring Cloud Data Flow documentation suggests rebuilding (!?!) when you need to add a custom JDBC driver. That seems a bit crazy to me so after a bit of Googling it turns out you can do it using the Spring Boot PropertiesLauncher:

java -cp spring-cloud-dataflow-server-2.2.0.RELEASE.jar -Dloader.path=lib \
   org.springframework.boot.loader.PropertiesLauncher \
   --spring.datasource.url=jdbc:mysql://localhost:3306/y9a?useSSL=false \
   --spring.datasource.username=root \
   --spring.datasource.password=password \
   --spring.datasource.driverClassName=com.mysql.jdbc.Driver
And you simply have to put the JDBC driver jar file in the lib/ directory:
spring-cloud-dataflow-server-2.2.0.RELEASE.jar
lib/
   mysql-connector-java-8.0.17.jar
Disco!

Tuesday, January 8, 2019

Building a WebSocket application with Spring Boot

This article shows you how to setup a very basic WebSocket application powered by Spring Boot. It uses the Java API for WebSocket (JSR-356) to define a WebSocket server endpoint and includes a very simple JavaScript WebSocket client.

As usual for a Spring Boot application, it all starts with the Maven POM:

  <?xml version="1.0" encoding="UTF-8"?>
  <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

      <modelVersion>4.0.0</modelVersion>
      <groupId>com.ervacon.sb</groupId>
      <artifactId>websocket</artifactId>
      <version>0.0.1-SNAPSHOT</version>

      <!-- Inherit defaults from Spring Boot -->
      <parent>
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-starter-parent</artifactId>
          <version>2.1.1.RELEASE</version>
      </parent>

      <!-- Add typical dependencies for a websocket application -->
      <dependencies>
          <dependency>
              <groupId>org.springframework.boot</groupId>
              <artifactId>spring-boot-starter-websocket</artifactId>
          </dependency>
      </dependencies>

      <!-- Package as an executable jar -->
      <build>
          <plugins>
              <plugin>
                  <groupId>org.springframework.boot</groupId>
                  <artifactId>spring-boot-maven-plugin</artifactId>
              </plugin>
          </plugins>
      </build>

  </project>
Notice how we pull in the spring-boot-starter-websocket dependency. This will make sure the Java API for WebSocket (javax.websocket) is on the classpath, together with Spring's support for WebSocket. When Spring Boot starts it will detect these dependencies and auto-configure the embedded Servlet engine (Tomcat by default) to handle the WebSocket protocol.

Next, we define our @SpringBootApplication class:

package com.ervacon.sb.websocket;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;

@SpringBootApplication
public class Application {

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }

    @Bean
    public ServerEndpointExporter serverEndpointExporter() {
        return new ServerEndpointExporter();
    }
}
The only special thing here is the fact that we register the ServerEndpointExporter, which will make sure our @ServerEndpoint annotated classes get registered with the WebSocket runtime.

We can now define our server side WebSocket endpoint:

package com.ervacon.sb.websocket;

import org.springframework.stereotype.Component;

import javax.websocket.OnMessage;
import javax.websocket.server.ServerEndpoint;

@Component
@ServerEndpoint("/socket")
public class WebSocketEndpoint {

    @OnMessage
    public String echo(String message) {
        return "Echo: " + message;
    }
}
Nothing to surprising here: we're just using the Java API for WebSocket (JSR-356) to define a method that will handle incoming text messages by simply echoing them back to the client. That client is implemented using some basic JavaScript code in a simple HTML page (add this as index.html to src/main/resources/static in your Spring Boot application):
<html>
  <head>
      <title>WebSocket Client</title>
      <!-- based on https://www.nexmo.com/blog/2018/10/08/create-websocket-server-spring-boot-dr/ -->
  </head>
  <body>
  <div class="container">
      <div id="messages" style="border: 1px solid gray; padding: 1em;"></div>
      <div class="input-fields">
          <p>Type your message:</p>
          <input id="message"/>
          <button id="send">Send</button>
      </div>
  </div>
  </body>
  <script>
      const messageWindow = document.getElementById("messages");

      const sendButton = document.getElementById("send");
      const messageInput = document.getElementById("message");

      const socket = new WebSocket("ws://localhost:8080/socket");

      socket.onopen = function (event) {
          addMessageToWindow("Connected");
      };

      socket.onmessage = function (event) {
          addMessageToWindow(`Got Message: ${event.data}`);
      };

      sendButton.onclick = function (event) {
          sendMessage(messageInput.value);
          messageInput.value = "";
      };

      function sendMessage(message) {
          socket.send(message);
          addMessageToWindow("Sent Message: " + message);
      }

      function addMessageToWindow(message) {
          messageWindow.innerHTML += `<div>${message}</div>`
      }
  </script>
</html>
We can now launch our Spring Boot application using a simple mvn spring-boot:run and test it by accessing http://localhost:8080/.

Monday, September 17, 2018

Software maturity

While reading a news article on Domain Driven Design over at InfoQ, I came across an interesting metaphor attributed to Eric Evans (i.e. the father of DDD):
A new metaphor Evans introduced was comparing a large software system to a community garden. Looking past the obvious bounded contexts of people sharing space in the garden, he sees positive analogies to legacy systems, looking at the "abundance of maturity." Gardens are most valuable in late summer, when they are most productive. However, that is long past the stage when you can easily make changes to the garden, in early spring. Similarly, the most malleable phase for software is not when it is the most productive.
This puts an interesting perspective on something I've noticed in my own career. Just like all developers, I love getting started on a new greenfield project: things are new and exciting and together with the team you're constantly inventing new ways of tackling problems and (re)factoring the system accordingly.

Some time after the first go-live, which typically comes with a host of teething problems, the application stabilizes and slowly evolves into a mature system over the next several releases. You can no longer reinvent the wheel at this time: you're building on the foundations you established earlier.
I've found that I also get a great deal of satisfaction from this phase: shepherding a system to maturity, seeing it come to fruition and hopefully realizing it's full potential! It as this time users are most happy with what has been delivered: the software is well understood and quality is still high, making changes predictable and quick.

Over time quality slowly deteriorates and accidental complexity grows. Changes become more difficult and risky, with more regressions slipping in. Ultimately the system is replaced with something new and better, bringing everything full circle, just like in a garden.