Reports from Devoxx PL

· Read in about 12 min · (2432 Words)
Posted in conferences / java / architecture / testing

From yesterday until tomorrow I’m attending Devoxx Poland (or Devoxx PL for short). It’s the second largest conference in the Devoxx family with around 2700 people attending. The conference is held in the ICE Kraków Congress Centre, a large venue with an amazing primary room.

Entrance of the ICE
Entrance of the ICE

The main reason I’m here is to give a talk about GraalVM on Wednesday morning. Apart from that, it’s a nice opportunity to network, meet old friends and make new ones. And of course to attend other talks to learn something new.

I’ve selected some of the talks I attended and share my impressions of them.

Testing your Messaging Application

Jakub Pilimon and Marcin Grzejszczak (both from Pivotal) started their session with a light-hearted sketch of typical consultancy habbits. In their sketch, one of them was an “ivory tower architect” at a bank, and the other one, being the consultant, was working as a programmer at the bank. The bank has a system to apply for a new bank card, but that system needed to be changed: it should send out notifications. Thankfully, there was already another system to do that, so it should be as easy as invoking some webservice on that other system.

This is what the “ivory tower architect” came up with:

Sending multiple notifications
Sending multiple notifications

This is how professionals solve these kinds of problems, becasue they get paid by the number of lines of code.

Thankfully that turned out to be a joke and the two started working out a message driven architecture. The decisions of the card application system are followed by publishing an event on a message broker (RabbitMQ in this case), which happened to be there already.

But being good “full stack developers” they of course want to test the integration with the message broker, too. The first approach used a mocked bean for publishing events, which made the test green but the application failed to start. The mock didn’t override an existing bean in the application, but that was only visible when the app was indeed started. Easy to fix, though, provide an implementation that actually sends out the event using Spring Cloud Stream.

Speaking of testing, Spring Cloud Stream provides a nice test feature that captures all messages sent to a Source and stores that in a BlockingQueue. This queue is then available within the test case so it can be inspected. This way, the integration tests run without a message broker but still use the exact same code and configuration as the production application will.

But how to synchronise the database transaction and the publication of the event? To make sure that either both or none of that happens, they don’t publish the event directly to the message broker. Instead, they first store it in the database. At a scheduled rate, the database would be queried and all events that arent published yet would be sent to the message broker.

All in all a nice and entertaining talk which had some good patterns and principles under the hood.

Further reading

A Case for Outside-in Desgin

Sandro Mancuso started his talk with a small throwback to the days of RUP. Back then, we were forced to designing our software systems at various levels before we could actually start coding it. But there would be no feedback from building the system, and it wasn’t possible to update the design based on implementation experiences.

Enter the agile way of working, where we do design later in the process and focus on getting the system to work. But this gives us a new challenge: we start to loose attention for good designs and the higher-level picture. By “just building”, our designs become a mess, and it’s very hard to recover from such a mess.

We all have software design biases.

When we learn a new language, it starts with struggling. But since we like the language, we push the boundaries and put ourselves to learning it. Eventually, we can build almost anything with it. It works the same way with software designs. You build a few systems according to a specific design, and they work well. From that moment on, you start to develop a subtle bias - those designs worked well in the past, and they will work this time. But worse, you develop an anti-feeling to other, unfamiliar designs.

Its easy to get obsessed with for example the domain model. It might not always fit the user interface needs, but it perfectly models the business data and processes. Of course we could put adapters in between, but isn’t that a bit selfish? Sofware is there to provide functionality to end users, not to only be a nice model. But since having a good model worked in the past, we stick with that, instead of thinking about alternatives.

Software design often only happens at the intersection of business priorities and technoglogy priorities. The process of only looking at the top priority backlog items makes it harder to look at the bigger picture of our systems as a whole. But it’s important to do that!

The goal of software design is to enable companies to continuously achieve their business goals.

Designing outside in
Designing outside in

It could make sense to look from outside the business or even outside the company to the system that you’re developing. If we wouldn’t be building it for this department or this company, but we would productise it: what would be the main features we’d announce? Those functions that will be in the “product box” are the important functional areas for the system. That should be the input for having strategic designs or architecture visions.

Then impact mapping can be used to translate those high-level ideas back into actions. Having this in a structured agreed way helps prioritising, and helps making all kinds of decisions. Not only related to software design, but also to way of working and planning.

GraphQL in Java World, let’s go for a dive

Vladimir Dejanović quickly dove into GraphQL. The one-line summary from the GraphQL website isn’t particularly helpful:

GraphQL is a query language for APIs

To be more precise, it is a specification for a query language. This means, if we want to build applications with GraphQL, we should also have an implementation. In this session, he used graphql-java. There’s also a collection of kickstarters for various platforms.

GraphQL requires you to have a schema, to describe what your data looks like. Once a query comes in, the first thing an GraphQL application will do is verify whether the query matches the schema. When building GraphQL server-side software, there are two approaches:

  1. Schema first where you first write a file that describes the model. Which entities are there, what attributes do they have. Then the code follows that schema by reading it at runtime and registering queries and resolves.

  2. Code first where you write code using the language and framework of your choice, and annotate in such a way that the runtime can deduce the schema.

One of the nice things of the schema-first approach is that it is easier to create a mock implementation. Of course it will not provide any useful data but it could be of use for consumers of your API.

Schema

A small schema snippet might look like this:

type Talk {
    id: ID!
    title: String!
    description: String
}

schema {
  query: Query
}

type Query {
  allTalks: [Talk]
}

This schema says there’s a query named “allTalks” which returns zero or more “Talk” entities, which will have an id attribute, a title attribute and maybe a description.

GraphQL queries can also have parameters. This can be useful for searching, but also for pagination.

Queries

The only thing that’s missing right now is a GraphQLQueryResolver. It’s a Java class (could be a Spring component if you use Spring) that knows how to execute the query and find the right data to answer it. It can of course delegate logic to other classes (or components) in your application, e.g. a database abstraction layer.

A GraphQL query gets sent by the client and is a flexible mechanism to ask for the specific data that the client is interested in.

query {  
  allSpeakers {
    id
    twitter
  }
}

This query would invoke the pre-defined allSpeakers query, but only get back the ids and the Twitter handles. The fact that the underlying datastore also has a name attribute for Speakers is not relevant for this client.

The server side needs to know how to run the allSpeakers query. When using the Spring-based kickstarter, it’s as easy as having a method like

@Autowired
SpeakerService speakerService;

public List<Speaker> allSpeakers() {
    return speakerService.findAll();
}

Mutations

Another useful thing is you can allow GraphQL clients to actually mutate (parts of) your data. For that you need to provide a class that implemements GraphQLMutationResolver. Just like the query resolvers, mutation resolvers can (and should!) delegate logic to other components in your application.

Subscriptions

Finally, GraphQL allows clients to subscribe to future changes. graphql-java does this on top of ReactiveX. This means that if a record changes, the client gets notified of that change. For this to work, you (obviously) need to implement a GraphQLSubscriptionResolver.

This also needs some conventions between the client and the server. There’s no formal specification yet for this, but many implementations do this over WebSockets. As soon as the client subscribes, the server will send back updates over a WebSocket and the client is notified.

Challenges

Conceptually, you can easily build a cyclic query by accident, for example where you ask for all talks of all speakers. There’s a few things you can do to protect your server from such queries:

  1. Define a maximum time each query is allowed to run.
  2. Define a maximum depth each query is allowed to ask for.
  3. Define a maximum query complexity each query is allowed to ask for.

Further reading

Mixed Paradigms: The Method to Madness

Last session I want to discuss here is one by Venkat Subramaniam. He started with a little philosofical question: Who are we? There are many roles that describe the work that we do, and there are even more function titles. But the one thing that unites us is that we solve problems. (Of course, some create more problems than they solve, but still…) And a small part of what we do every day is programming. Programming languages are a tool that we use to solve problems.

So what’s a programming language? Languages are a method of communication, be it to other humans or to machines. One of the ways to describe languages is in terms of being Turing complete. And although that might sounds relevant, almost all languages are. But not all of them are useful, and that’s what differs languages from each other. To be clear, this is not only about syntaxes. Syntax is there for the machine - we as humans need time to learn it. But semantics are much harder: what does the code actually mean? And that’s a lot harder to learn.

Type Systems

When it comes to type systems, there are two ways to describe programming languages

Classification of programming languages type systems
Classification of programming languages type systems

Here, the Strong / Weak axis describes whether type information is enforced at runtime. The Dynamic / Static axis describes whether type information is enforced at compile time.

Paradigms

Programming languages can also be classified in terms of the paradigm that they follow: Procedural, Structured, Object-oriented and Functional.

According to David Wheeler,

All problems in computer science can be solved by another level of indirection.

This would mean that any shortcoming in a programming paradigm could eventually be solved by adding another level of indirection. And indeed, we can see that most paradigms have an indirection that helps us solve many of the challenge we might face.

Levels of Indirection in programming paradigms
Levels of Indirection in programming paradigms

Unfortunately this classification has also lead to many misguided conversations: should you be doing object-oriented or functional programming? The real question is: are you writing imperative or declarative code?

In an imperative style, you don’t only describe what you do. You also need to explicitly specify how it should be done. It’s usually easy to write, but hard to read. Also, it tends to be progressively hard. As the complexity of a problem grows, the complexity of an imperative solution grows at an even faster pace.

In a declarative style, on the other hand, you focus on what to do. You don’t need to specify how to do it - you leave that to the underlying platform, or to the libraries that you use. Higher-order functions are a key element in doing that. Many experience this as easy to read, but rather hard to write. But isn’t this merely because of our unfamiliarity with the paradigm and the concepts?

Embracing Duality

Does this imply functional programming is better than imperative programming? Sometimes, concepts don’t really fit into just one definition or one box. Just like light can behave simultaneously as a particle and as a wave, we might as well mix imperative and functional paradigms.

Imperative code, for example, is good at dealing with side-effects and exceptions. Functional code, on the other hand, is good at dealing with scale, parallelisation and reasoning. Wouldn’t it be great if we could combine the powers of both paradigms?

If you have an imperative code base, and you want it to run in parallel, you need to touch a lot of spots in the code. It can easily become a synchronise-and-suffer codebase. The structure is completely different from its original, sequential counterpart, and you can hardly recognise what it does. But for functional code, this difference isn’t there. The structure will be roughly the same, and it better reflects the structure of the problem at hand. In some cases (like the Java 8 Streams API), switching from sequential to parallel processing is as simple as one method call. Mind you, there’s a lot of things to keep in mind when you do that. The main question is, whether the code is heavily using blocking I/O or instead computation heavy.

The nice thing is that in many cases you can switch styles even within one codebase. This allows you to use the tools that best fit the puzzle that you’re solving. And that’s what we do after all: solving problems.

Further reading

Impressions of Kraków

I didn’t do much sightseeing this time. Just a few pictures that might be worth sharing…

Sight over the Vistula river
Sight over the Vistula river
Wawel Royal Castle, seen from the Vistula river
Wawel Royal Castle, seen from the Vistula river

Comments