The truth about your “source of truth”

Too often when we design our microservices architecture*, we find ourselves wrapped around the axle, trying to define a single source of truth (or SoT) for our domain entities. Common wisdom has it that every domain entity in our enterprise should live in exactly one, centralized location. If you want to fetch an instance of that entity, you go to that location.

* note: I loathe the term “microservices architecture”, but that’s for another discussion

As with much common wisdom, this isn’t necessarily so. It’s a good idea to keep track of where our data resides, sure. But bending over backwards to define a single SoT for every piece of data is the wrong approach. At best, it’s often simply not necessary. Worse, it can cause more problems than it purports to solve. In fact, the very notion runs counter to the event-based systems that power many enterprises today. And as we’ll discuss, a true source of truth for our data is, by and large, mythical.

What’s wrong with a single source of truth?

Before we get into the problems of single SoTs throughout our microservices architecture, let’s revisit why we build microservices in the first place. We’ll start with one of the most fundamental relevant patterns: the Bounded Context. Much has already been written about the pattern. But the general idea is that each business domain should be thought of as its own self-contained system, with judiciously-designed inputs and outputs. And each is owned by a single, cross-functional team. The team builds, deploys, and maintains the services and applications that it needs to get its job done—with minimal dependencies on any other teams.

And this leads us to perhaps the most fundamental benefits of a microservices architecture. While we can rattle off the technical benefits of microservices, the true benefits are organizational. Teams have the control and responsibility over their own projects. They can make as many changes to their code as they want to, with little fear of breaking other teams. They can release as frequently as they need, without requiring coordination with any other team. And they own the entire lifecycle of their code, from design and development, to deployment, to production monitoring.

Enforcing a single source of truth for our domain entities sabotages those benefits.

Instead, single sources of truth re-establish dependencies across teams and across bounded contexts. They force teams to break out of their bounded context to perform work. If the team requires a change to an entity, it is now at the mercy of some other team’s schedule to get the changes made. And consider production issues. Teams will start finding themselves awoken at night, paged because some other teams’ service is misbehaving.

Single sources of truth also equate to single points of failure. Requiring all services to fetch User data from a single location means that our entire enterprise may grind to a halt if the User service goes down. SoTs also introduce performance and scalability bottlenecks. As the number of applications that need to access that User data grows both horizontally and vertically, the load on the User service will effectively grow exponentially.

But perhaps most importantly, adherence to single sources of truth severely hampers our ability to move forward with a workable architecture. This becomes apparent as teams wrap themselves further and further around the axle, arguing about the SoT for such-and-such entity. Have you ever found yourself pulling multiple different teams together to debate the design of your microservice’s API? Trying to meet every team’s requirements? Negotiating compromises when those requirements contradict each other? Yeah, me too. That’s a clear smell that we’ve been doing something wrong.

Why again did we bother with a microservices architecture?

And, what are we to do now?

Relax. And stop worrying about our “source of truth”. Because in all likelihood, the “single source of truth” we’re seeking doesn’t even exist.

The quest for the holy source of truth

At least, it probably doesn’t exist among our microservices. Let’s build a simple inventory management system. Our system will contain a service that tracks physical inventory items, stored in a physical warehouse. Every time a new item is delivered to the warehouse, its barcode is scanned, and the service updated to reflect the new amount. Thus, the inventory item service will be our reliable, single source of truth for our enterprise’s product inventory… right?

Except, how accurate will the database be after warehouse workers walk out with their pockets stuffed full of items at night? Or when a leaky pipe in a warehouse corner causes packages to slowly disintegrate? Or when a palette of new items is simply missed by the scanner?

Of course, many organizations have domains that don’t model their physical entities. So instead, let’s try another example. Say we’ve built a company that aggregates deals on hotel rooms. We partner with hotel chains, ingesting underbooked rooms from the chains, and presenting the best deals to interested would-be travelers. To do this, we have a HotelPartners bounded context that ingests room data from our partner hotels, and store it in a database.

So… is that database a SoT for that data? Not really. We’d sourced that data from its partners’ databases, which in turn are representations of the availability of their physical hotel rooms. Any “source of truth” for this data certainly doesn’t reside within our organization.

The point is, obsessing over the “one true source of truth” for our entities can be a fool’s errand. Often, there isn’t any such thing—at least, not in our enterprise system.

Embrace the fact that “sources of truth” are relative.

Instead of searching for the elusive absolute, single source of truth for anything, we should instead embrace the fact that “sources of truth” are relative.

Recognizing this fact is remarkably liberating. And it frees us up from the burden of trying to ensure that each entity in our domain has exactly one home, to which every service in our architecture needs reliable access.

Think in terms of Canonical Views and Scopes

Instead of a single SoT for our data, we can think in terms of the canonical view of data within a given scope. Within the scope of any system that stores data, there will be a data store that represents the system’s most up-to-date view of that data. That is the system’s canonical view. There may be additional data stores in the system that also provide views that data. Maybe the data is cached for quicker read access, or enhanced with data from another canonical source. But those additional data stores are always subservient to the scope’s canonical data source.

As an analogy, think of materialized views in a database system. The original table(s) from which the materialized views are derived represent the canonical view of the data, within the scope of the database schema.

Organizational and industry scopes

Let’s revisit the hotel room aggregator from a few paragraphs back. As engineers in this semi-fictitious organization, we fetch information about hotel rooms from our third party partner sources. The data enters our system, is transformed into our domain entities, and stored, all within our HotelPartners bounded context. Other product-focused bounded contexts in our organization then use that data for their own purposes.

Figure 1 – Our organization’s scope

So in the scope of our organization, this HotelPartners bounded context contains the canonical source of hotel room data.

New let’s zoom out and look at the industry as a whole. We’ve sourced this data from hotel chains. So in that sense, those chains’ databases become the canonical source for the data in the scope of the industry as a whole.

Figure 2 – Zoomed out to the industry’s scope

We can also zoom in and look at our organization’s specific bounded contexts. Much like our entire organization sources data externally and stores its own canonical representation, so can our bounded contexts. Specifically, they can source the data from the HotelPartners bounded context and store their own local copy.

Figure 3 – Zoomed in, to demonstrate that each vertical in our organization represents a scope, each with its own canonical source

Our product teams now have their own local canonical source of hotel room data. They are each free to enhance the data as they need. For example, the Search bounded context might also source data from a Reviews bounded context, in order to mix customer reviews with hotel information.

They can also store it in whatever format they see fit. They are not reliant on the HotelPartners team to make changes for them. Nor are they reliant on that team to maintain a particular SLA for their RPC services. The product teams are also free to create other secondary data stores for the data. For example, the Search team might set up a secondary data store, say an ElasticSearch index, to support searching data across various axes. This secondary data store would still be sourced from Search‘s canonical data store.

Meanwhile, the HotelPartners team is not burdened with creating and maintaining a “one-size-fits-all” data model, in which they try to make every product team happy in terms of the data fields that are stored.

Events shall set you free

If you’re not familiar with event-based systems, you might be wondering how a product-oriented bounded context (e.g. Search) is supposed to derive its data from its enclosing scope’s canonical source (i.e. Hotel Partners). Wouldn’t it still need to make calls into Hotel Partners‘ API services?

As it turns out, it doesn’t. Instead, the canonical source publishes its changes as events. Generally, we use an event log like Kafka for this purpose, but the details don’t matter here. What does matter is that the product-oriented bounded contexts are able to subscribe to those events, ingest them, and store the results in their own data stores.

Figures 1 through 3, then, are a bit too simplistic in depicting how the Booking and Search bounded contexts derive their data. Figure 4 provides a more accurate look.

Event passing
Figure 4 – Bounded contexts using events to derive data from our organization’s canonical source(s)

The Hotel Partners bounded context ingests data from the external partners and saves it into its Rooms database. Once it saves the data, it creates events that describe the saved data, and publishes those events to an event log. The Booking and Search bounded contexts subscribe to that event log and, as the events come in, consume them and populate their own data stores.

There are a few other items of note. First, the Search bounded context uses the same event-based mechanism to propagate changes from its own canonical source (titled “Room Details” in the diagram) to its secondary search index. Also note that the Search team is subscribing to another event log, titled “Event Log: Reviews” in the diagram, in order to ingest hotel reviews. Although not depicted in the diagram, we can assume that hotel reviews are published to the event log by a different bounded context (perhaps that bounded context allows users to enter reviews, or it ingests reviews from third-party sources).

We commonly refer to such systems as event-based systems. They are distinct from the more traditional request-response systems that are powered by synchronous API calls, and that tend to drive the desire for single sources of truth. Event-based systems also inherently imply eventual consistency. What this means is that at any given point, data across our organization may be in different states. Absent any new changes, the state will converge. Since change is generally a constant, this means that for some periods of time (generally measured in milliseconds) a given entity might be more up to date in one context (say, Hotel Partners) than it is in another context (e.g. Search).

The good thing is that in our well-designed systems, with proper bounded contexts, eventual consistency is perfectly acceptable. Back to our example, the Hotel Partners bounded context will first ingest data. The data then flows—at roughly but not exactly the same time—to both the Booking and Search bounded contexts. At this moment, a given entity may be out of sync between each bounded context. However, since each bounded context represents a separate business domain—with its own applications and functionality—brief inconsistencies become mostly unnoticeable.

If you must obsess, then obsess about your data’s Originating Source

Whenever we attempt to define a “single source of truth”—that is, a single location from which to internally fetch an entity—I suspect that our brains are subconsciously seeking a single “originating source”—that is, a single location from which to externally ingest an entity.

In other words, any given entity should enter our organization via a single location. It follows naturally that the data will flow through our organization from that entry point. Moreover, it will flow in a single direction.

Let’s take Figure 3 from the previous section (with the understanding that we’re using an event log like Kafka to propagate data). There, we see that room availability data is ingested into our organization from external sources by the Hotel Partners bounded context (perhaps via batch file ingestion). The data is distributed to our org’s various bounded contexts (e.g. Search, Booking, etc).

Now let’s say that Search has added a new web application, allowing registered individuals to add room inventory into our system. Suddenly, we have two locations in our system in which we ingest the same data.

Figure 5 – It gets confusing when the same data comes from multiple sources

Why is this a problem? The complexity of managing the flow of data in our system has dramatically increased. If we have data coming in through both Hotel Partners and Search, both bounded contexts will need to publish their incoming data as messages. And of course, both will need to consume each others’ messages and make appropriate changes. For example, Hotel Partners will need to consume messages from Search, and update its database. Should it then publish that change as a message, which Search would subsequently consume? If we’re not careful, we’ll create an infinite loop of messages. What about Booking, which now needs to consume messages from both Hotel Partners and Search? Is it now responsible for sussing out from which service the data originated?

Next, consider conflict resolution. If someone uses Search’s web application to push data that conflicts with other data that we’ve ingested from industry sources, who decides how to resolve those conflicts?

Similar to conflict resolution, we have the issue of deduping. If we receive data from multiple sources, odds are that we’ll routinely ingest duplicate data. If this data enters our system in multiple places, where would this deduping take place? We’ll discuss deduping a bit more in the next section.

So we should be cognizant of where our data originates. And if at all possible, we should limit any given entity to a single originating source. If we truly need to allow users to add room availability data to our system, we should allow that only within the same scope in which we ingest bulk purchase data (that is, the Hotel Partners scope). That way, our data flows in a single direction, and propagation becomes much easier to reason about and much less error prone.

Figure 6 – Much better!

And now for some FAQs

As I discuss the concept of relative SoTs and context-based canonical sources of data, a few questions tend to arise. Let’s discuss them here.

What about Master Data Management?

I’ve been asked whether this idea of canonical data sources runs counter to the industry practice of Master Data Management, or MDM. Put simply, the use of MDM helps organizations ensure that they do not have duplicate representations of the same piece of information floating around various internal groups. Importantly here, MDM implies that an organization must have a single canonical source of every entity in the company’s business domain.

Despite first appearances, MDM doesn’t run counter to the idea of relative canonical sources. As discussed above, we will still have a canonical source of its data, within the scope the organization as a whole. Here, for example, we would dedupe records and assign them a unique ID. In turn, the records stored within the other bounded contexts can (and should) retain the entities’ canonical IDs.

Meanwhile, the MDM data store can and should remain relatively lightweight. In fact, it’s perfectly feasible for such data stores to house little beyond entity IDs, and perhaps some other basic identifying information.

What about single versions of truth?

Sometimes we find ourselves with different applications that perform their calculations on their own view of common data. In such cases, wouldn’t we be leaving open the possibility of differences between the calculation algorithms? We might wind up displaying different results for the same data across different applications.

This is related to the concept of a single version of truth (SVoT). This concept states, roughly, that if multiple systems have their own view of the same data, there must be one agreed-upon interpretation of the data. Often SVoTs are referenced in the context of business analytics and decision-making, but is also applicable when discussing distributed systems as we have been here.

The truth is that while we don’t often need to worry about creating a single source of truth for our data, we sometimes need to define a single SoT for our algorithms.

For example, our hotel-room-aggregation organization might provide ranked recommendations for would-be travelers. If the recommendations can appear in multiple places, then we’d want to show the same recommendations for a given user, no matter where they appear, or from where the raw data was sourced. From that standpoint, while the data can live in multiple locations, we need to ensure that a single algorithm is used to perform the calculations.

How do we ensure a single SoT for a given algorithm or calculation? We have a few options.

Distribute it in a library

Our first thought might be to write the algorithm, package it up into a library, and make it available for import by all of the services that need it. That way, all of the services have a consistent, local mechanism with which to calculate their local data.

Calculation library
Figure 7 – Here, each bounded context that needs to perform a particular calculation has imported a common library

While this approach may sound simple and appealing, reliance on libraries in a microservices architecture has some major drawbacks:

  • Coordinating changes is difficult. Let’s say the algorithm changes. How do we distribute that change to all of the dependent services? We need to build and deploy a new version of the library. Then, we need—in a coordinated fashion—to redeploy all of those services. This breaks a fundamental tenet of microservices: independently-deployable services.
  • We’ve tied ourselves to a single language. We may have decided to allow our teams to use different programming languages or platforms in our enterprise. However, we can only write a single library in one language. If we repeat the algorithm in different languages, then we no longer have a single SoT for that algorithm.

Deploy a microservice to perform the calculation on the fly

We can deploy a microservice to perform the calculation. In this case, we’d require applications from various bounded contexts to synchronously call in to this service to get the calculated data.

Of course, with this approach, the raw data might as well reside solely within a data store managed by this microservice. So we could write this service as a consumer of the messages produced by Hotel Partners.

Calculation Service
Figure 8 – Here, we’ve adopted the typical approach of requiring each bounded context to synchronously call a microservice that will perform the calculation for them.

While this approach may be preferable to a library, it gets us back to the original issue that we’ve looked at in this article. We now have enforced a single source-of-truth for our calculated recommendations.

Deploy a microservice to publish the results of the calculation

So then, why can’t our new microservice simply perform calculations as hotel room availability changes, and publish those calculations for different bounded contexts to consume?

That is, after all, how event-based systems work.

With this approach, our new microservice becomes a consumer of the data produced by Hotel Partners, just as our other bounded contexts’ microservices are.

Calculation Events
Figure 9 – Here, we’ve adopted the same event-based pattern that we’d discussed earlier. Our Calc service consumes data, performs calculations on it, and publishes the results for other bounded contexts to consume.

Are we saying that a single source of truth is always a bad idea?

We’ve seen that the enforcement of a single source of truth is often unnecessary, detrimental, and in many cases, a fallacy that we’re trying to make reality. Still, we may encounter some data for which we want to enforce a single SoT. As we discussed above, eventual consistency may not be acceptable for certain data. We may have services that are infrequently accessed, are not in any critical paths, and have simple, infrequently-changing APIs.

Authentication is a common case. Typically, we want a single location to manage a user’s login credentials, roles, permissions, and tokens. Generally these activities happens (relatively) infrequently, but we want to be sure any updates are reflected immediately. We therefore might opt to define a single source of truth for our authentication data. Even in this case, however, we would want to be sure that we keep the authentication model as light as possible. User details such as name and contact information, for example, would go elsewhere.

But this is not a universal, golden rule to which we must adhere. Despite common thinking, single sources of truth for our data are not an automatic requirement. Moreover, they are often a hindrance in terms of both productivity as well as application performance and scalability.

Designing our systems instead with canonical sources and scopes in mind will help us avoid bottlenecks, allow us to design more flexible, scalable event-based systems, and allow our teams to focus on getting stuff done.

No, I swear, my iOS app doesn’t crash on startup! A simple lesson I learned as an enterprise architect about indie iOS game development

Although I’m focusing on enterprise software architecture (a.k.a server side development) by day, I still write Swift-based iOS apps on the side. Though the two software engineering disciplines can be quite different, they sometimes share more in common than I give them credit for.

The other day I submitted a minor update of my Word Wad iOS game to the App Store. The changes were mostly cosmetic: adding back an image that I’d discovered had gone missing, and introducing auto layout in the app’s storyboard. Other than that, I hadn’t made much in the way of code changes.

I tested my changes out on a number of simulators, as well as on my physical iPhone. Everything looked great, so I upped my app’s version number, created a build, validated the build, and ultimately submitted it. Expecting a quick win, I was surprised when, a few days later, I received a rejection from Apple stating, among other things, “Your app crashed when we launch the app”.

Word Wad screenshot
My app, Word Wad, in its non-crashed form.

Feeling a bit foolish for somehow submitting such a buggy app to Apple, I quickly launched Word Wad in a simulator. It ran fine. Figuring that maybe a fresh install would crash, I reset the simulator’s settings and tried again. No crash.

I tried a few different simulators, all with the same result.

Growing a bit concerned, I then connected my iPhone to my MacBook, deleted the previous copy of Word Wad, and launched Word Wad on my phone out of Xcode. Still no problems! I tried my iPad. Same thing.

So far, I couldn’t reproduce the crash. So how was I to fix it? As it turns out, Apple included a crashlog along with the rejection notice. So I opened it up to see how helpful it would be. (Letter Shuffle, you might guess from the crashlog, was Word Wad‘s original name).

{"app_name":"Letter Shuffle","timestamp":"2019-07-31 11:32:24.34 -0700","app_version":"1.3","slice_uuid":"b4a1df49-91b3-3d1d-a64f-a8d6bc44f649","adam_id":1219410678,"build_version":"6","bundleID":"com.taubler.lettershuffle","share_with_app_devs":false,"is_first_party":false,"bug_type":"109","os_version":"iPhone OS 12.4 (16G77)","incident_id":"DAB41DAD-9143-42B0-9115-7AB25B8FC81F","name":"Letter Shuffle"}
Incident Identifier: DAB41DAD-9A48-42B0-9115-7AB25B8FC81F
CrashReporter Key:   2b4341567d2bf6ee1c2ada8d72b26769dd6a74ff
Hardware Model:      xxx
Process:             Letter Shuffle [57833]
Path:                /private/var/containers/Bundle/Application/98F17500-4B54-4D89-8E2A-8B4F4A6B7D163/Letter Shuffle.app/Letter Shuffle
Identifier:          com.taubler.lettershuffle
Version:             6 (1.3)
AppStoreTools:       10G3
Code Type:           ARM-64 (Native)
Role:                Non UI
Parent Process:      launchd [1]
Coalition:           com.taubler.lettershuffle [5459]
Date/Time:           2019-07-31 11:32:24.3585 -0700
Launch Time:         2019-07-31 11:32:24.1648 -0700
OS Version:          iPhone OS 12.4 (16G77)
Baseband Version:    7.80.04
Report Version:      104
Exception Type:  EXC_BREAKPOINT (SIGTRAP)
Exception Codes: 0x0000000000000001, 0x000000010024f19c
Termination Signal: Trace/BPT trap: 5
Termination Reason: Namespace SIGNAL, Code 0x5
Terminating Process: exc handler [57833]
Triggered by Thread:  0
Thread 0 name:  Dispatch queue: com.apple.main-thread
Thread 0 Crashed:
0   Letter Shuffle                0x000000010024f19c 0x100228000 + 160156
1   Letter Shuffle                0x00000001002474e0 0x100228000 + 128224
2   libdispatch.dylib             0x00000001c64cc7d4 0x1c646c000 + 395220
3   libdispatch.dylib             0x00000001c646feb8 0x1c646c000 + 16056
4   libswiftCore.dylib            0x00000001f44b3e40 0x1f420d000 + 2780736
5   Letter Shuffle                0x0000000100245798 0x100228000 + 120728
6   Letter Shuffle                0x000000010024563c 0x100228000 + 120380
7   Letter Shuffle                0x00000001002453f4 0x100228000 + 119796
8   Letter Shuffle                0x0000000100245520 0x100228000 + 120096
9   UIKitCore                     0x00000001f2b23224 0x1f2810000 + 3224100
10  UIKitCore                     0x00000001f2b23628 0x1f2810000 + 3225128

<snip>

Binary Images:
0x100228000 - 0x10026bfff Letter Shuffle arm64  <a4b1de4970b3301da64458d6bc22f513> /var/containers/Bundle/Application/96F17500-2B52-4D89-8E2A-B4F4A6B7D76E/Letter Shuffle.app/Letter Shuffle
0x100688000 - 0x1006dffff dyld arm64  <06f3d9add3a233cea57df42b73686817> /usr/lib/dyld
0x100748000 - 0x100753fff libobjc-trampolines.dylib arm64  <065bd8006d513c358dc14e2a8ff1ba31> /usr/lib/libobjc-trampolines.dylib
0x1c5bf6000 - 0x1c5bf7fff libSystem.B.dylib arm64  <8a05d5f48f0a376abe6bd1caf4fc8138> /usr/lib/libSystem.B.dylib

<snip>

Completely unsymbolicated, and nearly useless as-is. So I went online to figure out how to make the logs more useful. I found this Apple tech note which discusses a command-line tool called atos. atos  helps symbolicate crashlogs, via one long terminal command.

So I opened up a text editor and got to work in crafting the command. The tech note provides the details, but based on my crashlog, I created the following:

atos -arch arm64 -o /Users/dave/Library/Developer/Xcode/Archives/2019-07-31/Letter\ Shuffle\ 7-31-19\,\ 10.32\ PM.xcarchive/dSYMs/Letter\ Shuffle.app.dSYM/Contents/Resources/DWARF/Letter\ Shuffle -l 0x100228000 0x000000010024f19c

To figure out the path to the executable, I went to Xcode and opened the Organizer (Window > Organizer). Under the Archives tab, I control-clicked the troublesome build and selected Show in Finder. Then I control-clicked the .xarchive file that appeared in the Finder and selected Open With > Terminal. A that point, simply entering the pwd command in the terminal got me the path to the file; I needed to add the escape characters myself, unfortunately.

Running the above atos command yielded this:

GameRound.init(number:letters:phrase:numRows:numCols:mixMoves:) (in Letter Shuffle) (<compiler-generated>:0)

Okay. So I had the offending function (GameRound.init) but not the exact location within that function. At that point, it was a matter of looking through the code and guessing where the problem could be. One aspect struck me in particular: an array of items is passed to the init method, along with a number of rows and columns used to turn the array into a matrix. However unlikely, it was conceivable that the array length wouldn’t match the number of rows times the number of columns, which would result in an array out of bounds exception. Figuring that could be the problem, I added some defensive code, and throughly tested my changes in a number of simulators and devices. Encountering no errors, I created a new build and submitted it.

Apple reviewed the build a few days later. And they encountered the exact same thing:“Your app crashed when we launch the app”.

sigh

At this point, I was flying blind. I had limited insight as to where the problem was occurring, with no insight as to what the problem actually was. Worse, try as I might, I was unable to reproduce the problem myself. I was worried that I’d have to use Apple’s review process to test out my blind attempts at solving the problem. Which, clearly, was not a sustainable approach.

The it occurred to me: rather than plugging my iPhone into my laptop and then launching an executable from Xcode, maybe I ought to run the build itself that I’d submitted to Apple on my device. So I did, via the following steps:

  1. Open up Xcode’s Organizer, as described above
  2. Under the Archives tab, select the troublesome build and click the Distribute App button
  3. Choose Ad Hoc as the option
  4. Follow the remaining prompts, ultimately saving the archive file locally
  5. Plug the iPhone into the laptop, then open the Xcode’s devices window (Window > Devices and Simulators)
  6. Select the iPhone in the left-hand pane.
  7. Drag the .ipa file from the Finder into the INSTALLED APPS section in the right-hand pane.

Doing that installed the specific build on my iPhone. I launched it, and… bam! The app crashed.

Woo hoo! I could now get my app to crash reliably.

So the problem seemed to be not with the code itself, but with the compilation process.

Now that I could get my app to crash on my own device, the next step was to look at the newly-generated crashlogs. Although I was able to access the new crashlog directly from my phone, it didn’t provide me with any more information as to the precise cause of the crash. However, I would be able to take some guesses at how to fix the problem, and test those fixes out locally before submitting the new builds to the App Store.

The crash still seemed to be occurring around the GameRound.init method, so I went back through that code. It turns out I was performing some superfluous operations that I really didn’t need to be doing, so I removed that code, streamlining it a bit. Then I created a new build, and distributed it locally as a new Ad Hoc archive. I installed the archive on my phone again, launched it, and… it crashed again.

But looking at the new crashlog, a different class—NullGameRound—was referenced at the top of the call stack. NullGameRound is an implementation of the “null object” design pattern, extending my app’s GameRound class and effectively overriding certain functions with no-op implementations.

Was there something about this pattern that the compiler didn’t like?

Although it’s a handy pattern, I was easily able to remove the use of NullGameRound in my game. So I did that, created and installed a new build on my phone, and ran it.

And it worked.

The moral of this story isn’t to avoid using the null object pattern in your Swift projects (although… really? Why should that have caused a problem?) It’s that sometimes, the Swift compiler will introduce problems into your distribution builds that won’t be present in your test builds.

For me, Swift and iOS development is something I do for fun, after hours. As I mentioned earlier, I work on enterprise services by day. With the advent of container technologies (e.g. Docker and Kubernetes) with which to deploy our backend services, it’s become a no-brainer that we test the same exact executable that will ultimately be deployed to production.

In hindsight it seems obvious, but the same should be true of mobile development, even for part-time indie developers like me. In retrospect, I was probably lucky that the compiler introduced a full-blown crash scenario that the Apple review team ran into, rather than a less apparent but more insidious bug that would’ve passed App Store approval, only to plague my app’s users later down the line.

I’d since submitted the fixed build to Apple, and the reviewed and approved it. But if I’d run the same build that I’d planned to submit to Apple before actually submitting it, I could’ve saved a lot of time and stress, and not inadvertently treated Apple’s reviewers as beta-testers of my app.

The Role of a Designer in an Engineering Organization What can HGTV teach engineers about engineering teams?

Awhile back, I had an interesting conversation with a product designer. She was, at the time, relatively new to our company and wanted get my opinion on a few things. We met for coffee, and talked a bit about our thoughts on the company in general. Then she posed a question to me: “What do you think the role of a designer is in an engineering organization like this?”

I paused for a moment, trying to come up with a useful answer. I’ve partnered with a number of designers throughout my career. And while there have certainly been similarities between these working relationships, each one has been fairly distinct. Apparently I was taking awhile to mull over the question, so she prompted me again:

“I talked to another engineer, who used the metaphor of building a house. He described engineers as being the architects who design the house, and the construction team that builds the house. So then, design’s role is to paint the house afterwards.”

“No,” I immediately told her, “that’s not right at all.”

The look of relief on her face told me that I’d given her the response she was looking for. Still, I wanted to answer her original question. And, not being one to mix metaphors, I wanted to stick with the home-construction theme.

Maybe TV can teach us something

So of course I turned to Love It or List It, a television show that airs on HGTV. Love It or List It has a simple premise: each episode features two (usually married) homeowners. Both are dissatisfied with their current home, but they disagree on how to solve the problem. One invariably wants to sell their home and move into another; the other insists on remodeling their current home. And thus, a sort of competition arises between the two “stars” of the show: real estate agent David, and designer Hillary. David tries to find the couple a brand new home to move into (and thus, “list” their current home), while Hillary remodels the couple’s existing home–or as much of it as she has budget for–to try to convince the couple to stay (aka “love it”).

It is Hillary that we are, of course, interested in here. From the outset, she works with the homeowners to identify their current situation: their problems with the current home, the requirements each would have in order to stay, their budget, etc. From there, she begins to generate ideas on how to renovate the home.

Importantly, she always collaborates closely with contractor Eric. While one might expect a contractor to simply carry out the designer’s vision, that’s not how Hillary and Eric typically interact. Instead, they work together to refine the designs and come up with the ultimate plan. For example, Hillary might float the idea to build a laundry room in a certain unused corner, only to told by Eric that the house’s existing plumbing would make that unfeasible… but hey! If we removed this other wall, then that would open up the perfect spot for a washer and dryer. And so on.

Once the designs are finalized and approved, we see glimpses of Eric and his team performing the actual demolition and construction. But collaboration between the two rarely ends there, as unexpected problems are encountered and they work together to resolve them. The end of every episode results in a stunning redesign, one of the homeowners invariably exclaiming “I can’t believe this is the same house!” and–at least so it seems to me–the homeowners typically electing to once again love their home.

Design is more than making things pretty

Painting a house
Not really the role of a designer

Or course, Love It or List It isn’t perfect metaphor for engineering organizations. For example, we might assert that Hillary’s role encompasses product management as well as design. But it’s a far cry better than the notion that designers are there to simply make a nearly-finished product look pretty.

Let’s take a step back and examine why I chose the Love It or List It metaphor over the slap-a-coat-of-paint-on-it metaphor:

  • First, designers don’t wait for engineers–or any other functional role–to decide what to build and how to build it. Instead, they help to drive the product definition before any line of code is written.
  • This means, of course, that designers don’t just come in at the end of a project. Instead, they collaborate with the entire team from the outset of the project.
  • Finally, design is about far more than just making things look good. While creating visual appeal is generally a big part of design (and for sure, Hillary’s remodels look stunning!) designers are there to contribute their understanding of user behaviors, and to help the product team to push the boundaries of how to solve their needs.

The designer and I riffed on this for a bit. By the end of our coffee break, we were both pretty happy with this metaphor. Weeks later, she and I soon found ourselves on the same team. We pushed this sort of cross-functional collaboration from the beginning. Our entire team–design, front/back-end and mobile engineering, product management, research, QA–would routinely meet to iterate over mockups, building on each others’ ideas until we’d found the best possible solution.

The end result? Solutions that were innovative, feasible, and I daresay in many cases, stunning. And for what it’s worth, team members who chose to love their jobs, rather than shopping for new ones.

Software engineers don’t need to sell themselves… right? Despite what you may think, companies don't hire every engineer that they find

As a software engineer in this market, you can land a job anywhere you please. Right?

I’ve been thinking a lot about hiring engineers lately. As I’m currently director of engineering at a medium-sized organization, I’m always on the lookout for good software engineers to woo and hire. Moreover, our company is planning to ramp up its hiring effort in the near future. It will be daunting, because the market for engineers is hot, and we’ll be facing a lot of competition from other companies that are also hiring. So that basically means that I’ll be hiring any decent engineer that comes my way, right?

Not a chance.

I’ve been on the other side on the equation, as an engineer. And I know the mentality. My skills are valuable… nay, they’re hot! Any company I’d decide to sign up with would be lucky to have me! I could walk into pretty much any company I pick and leave with a job offer!

As a hiring manager, I can tell you that that’s not true. I don’t know you. My team doesn’t know you. And during the interviewing process, we will have a limited time to get to know you. Everyone you’ve ever worked with might agree that you’re the best engineer they’ve ever come across, but you are–unfortunately–back to square one when it comes to me and my team.

Here’s another secret: with most hiring teams, when it comes to assessing your viability as a candidate, it’s not all about your technical skills. It’s not even mostly about your technical skills. Will code for foodDon’t believe me? The next time a candidate comes into your organization for an on-site, ask the interviewers what their main decision factors were, yay or nay. While there is generally a technical baseline that candidates must meet, the decisions almost always come down to things like:

  • Personality
  • Potential fit within the team
  • Ability to learn and adapt
  • Eagerness about the company and what it’s doing

In Building Great Software Engineering Teams, Josh Tyler describes asking his engineering team to outline their most important qualities in a candidate. While the answers varied across team members, among the top answers were that the candidate:

  • can teach the team something
  • loves programming and technology
  • is pleasant to be around

Nowhere at the top of the list was anything about being able to write out algorithms on a whiteboard.

Why should you care?

You’ve been to plenty of interviews in your career, and you’ve landed good jobs. If you were to lose your job in today (particularly if you’re living in one of the many hot engineering markets), you could probably land a job within a week or two. You’re doing fine in your interviews, so why change anything?

Well, let me let you in on another secret: hiring is not binary. Candidates are not simply grouped into yes‘s and no‘s. There are, of course, candidates that we don’t outright want to hire. Other candidates that aren’t quite at the moment, but we might consider in the future. There are of course candidates that we would like to hire.

And then there are the candidates that we need to hire.

You, of course, want to be in that last category. Because here’s yet another secret: once you’ve landed in that category, you’re in control of the hiring process. You’re much more likely to command whatever it is that you’re looking for. A higher salary. More equity. A signing bonus. Lenience in your starting date. Extra vacation. Even a higher role within the organization (which presumably, would carry higher financial incentives).

Once you’ve landed in that category, you’re in control of the hiring process.

In other words, there is a huge difference between a team’s grudging agreement to make you an offer, and a team’s insistence that it must do whatever it takes to get you onboard.

Also, consider your career trajectory. Many engineers enjoy good, comfortable careers having never risen about the level of Senior Engineer. But to get much beyond that level, non-technical skills become much more important. Competition becomes tighter, and we hiring managers become more picky.

So what should you do?

In preparing for interviews, most candidates focus on honing their technical skills. That is, of course, not a bad idea. After all, you will be assessed on them… in part. But you should focus equal time, if not more, on being able to sell yourself as a person to the interviewing team. You might have memorized how to reversed a linked list. But will you be able to convey your enthusiasm for programming? Are you prepared to demonstrate your ability to learn? Will you be able to teach your interviewers something?

Your interviewing preparation should start with one premise: You are interviewing the company just as much as they are interviewing you.

We place too much emphasis on the notion that interviews are all about the candidate attempting to prove him or herself to the interviewers, in hopes of receiving a job offer. But the inverse is also true. If you’re worth your salt as a software engineer, then you should be just as picky as they are. You should insist that the company sell itself to you.

If you adopt that mindset, everything else will start falling into place.

Be choosy about whom you interview with

In fact, this mindset should come into play before you begin talking to any companies. It should guide your decision as to which companies you’re even willing to talk to. Look for companies that are doing things that excite you. If you’re contacted by a recruiter, do some research on the company that they’re pitching. Does their work sound interesting to you? Is it an industry you know anything about, or care about, at all? Find out what technologies the company is using. Do you have any special expertise or interest in those technologies?

In other words, how much of an intersection is there between the company and what you want to be doing?

If there is little intersection, then you’ll find yourself at a disadvantage in talking with these companies. They also, clearly, might not be the right companies for you to join in the first place. Conversely, if there is a lot of overlap, you’ve potentially found a company that you would enjoy working at, and for which you’d be better positioned to prove yourself.

Think of phone screens as conversations

You’ve found a company that is doing things that excite you. You’ve set up your first interview. More than likely this will be a so-called phone screen, maybe with a recruiter or perhaps with an hiring manager.

What usually happens in these screens? The interviewer asks you questions, and you answer them, right? What if, instead, you asked them the questions? Not for the whole conversation, of course; certainly the interviewers have things that they want to know of you. But your answers should segue into questions for them.

For example, if you’re a Java programmer, you might be asked the latest version of Java you’ve used. Your answer should go beyond simply stating the version number. Talk about some of the features introduced in that version. If you were involved with moving standardizing your organization on that version, tell them about that. Moreover, ask them what version they are on. If it’s not the latest version, why haven’t they upgraded? If it is the latest version, why did they upgrade?

Ask your interviewers meaningful questions

Most interviewers will reserve some time–usually at the beginning of the session or at the end–for questions. This time is is generally very short, so you should ensure two things:

  1. you don’t squander the time asking trite questions, and
  2. you ask questions throughout the session

Don’t ask trite questions

I can’t tell you how many candidates have asked me “what’s your tech stack?” This question tells me a couple of things about the candidate.

First, that they’re not willing to apply much thought or creativity when it comes to solving a problem (the problem, in this case, being what question to ask me). Second, that they couldn’t be bothered to do the minimal amount of research that it would’ve taken to answer that question on their own.

It may be that you truly want to talk about the company’s tech stack, if there is something noteworthy that ties to your own experience or interests. Here are some better ways to broach the subject:

  • “I noticed that you use Go as well as Node.js. I’ve mixed both of them in projects as well. What are the use cases here for each of them?”

or

  • “I’ve heard that RxJava is used here. That’s exciting to me, since I’ve been using it on some of my own projects at home. Why was it adopted here?”

Remember, this is your chance to convey your overall love of technology and excitement at the prospect of working for the company.

Ask questions throughout the session

Interviewing sessions are usually short. With any given interviewer, you’ll likely have somewhere between thirty to ninety minutes to make an impression. During this time, the interviewer will, naturally, do most of the question-asking. And of course, you should answer all of the questions directly and fully. Ducking questions and avoiding what they’re asking never comes across well.

But remember, you are also learning about them during that period of time. So after answering a question, consider pivoting to ask them a similar question. For example, if you’re asked about your experience with building microservices, you might end your answer by asking things like, “How did this company get started building microservices?” or “How are you handling errors that occur during event consumption?”

This is an effective way to show that you are interested in what the company is doing, and to tie your interests and experience to what the company needs in a candidate.

Learn something

Most interviewers also want to be sure that you’re willing and able to learn things. This starts with asking good questions, and continues with attentive listening to the answers to those questions. Repeat back to your interviewers what you’ve been told, paraphrased, to show that you’ve understood.

I took an improv comedy class once. Comedians that perform improv sketches (so I was told, anyway) rely on the notion of “Yes, and”. This refers to how the comedians interact with each other. One comedian will start acting out a certain topic. The next comedian will join in and say (figuratively if not literally) “yes, and…” and continue the first comedians line of thought.

While I wouldn’t suggest breaking into comedy during an interview, I highly recommend adopting this mentality. Listen to what your interviewer has to say, demonstrate that you’ve heard them, and then build on what they’ve said.

For example…

Them: “Our company has been deploying ReST services for awhile, but we’ve started running into problems when we need to modify our APIs.”

You: “Yes, it’s easy to run into issues when making API changes to services that are out in production. I’ve found that the ‘expand and contract’ pattern  can help with this…”

In this way, you’ve learned a bit about the company (don’t forget, you’re there to interview them too!), you’ve shown that you’ve listened and learned, and you’ve set yourself up for your next objective…

Teach them something

Most interviewers are also looking for candidates that they themselves, in turn, can learn from. So make it a point to teach something to each of your interviewers.

This can happen as a natural extension of asking intelligent questions. Per the example in the previous question, you might ask your interviewer how its company handles errors during event consumption between microservices. Depending on your interviewer’s answer, you’ve got a pretty good opportunity to tell them how you have solved the problem in the past, and make suggestions as to how they might approach the problem.

Of course, a typical engineering interview session will involve the interviewer asking a technical question, handing you a dry-erase marker, and pointing you to a whiteboard. Often you’ll hear advice that you should keep talking through your solution. This is sound advice. But if you’re focused on merely talking, it’s easy to digress into muttering to yourself.

Instead, picture yourself as the teacher–you are, after all, standing at a whiteboard with a pen and an attentive audience! Talk to your interviewer as if you’re explaining to them how to solve the problem. This shift in attitude will ensure that you’re talking to them, and will help you project a level of confidence and authority.

It’s a different mindset, and takes practice

Thinking of an interview as a conversation that you help steer… well, that takes a while to get used to. But it’s the mindset that will help convey that you’re the engineer that companies not just want–but need–to hire.

CANWEALLAGREETHATWEWILLAIMFORCLARITYASWENAMECLASSES? CanWeAllAgreeThatWeWillAimForClarityAsWeNameClasses?

I like to be productive and efficient. So anything that unnecessarily wastes even just a few moments of my time… well, it really bugs me.

That is why I am particularly bugged by a particular practice (nay, an anti-practice) that I see too often. FYI, AFAIK most folks would agree with me, so IDK why this anti-practice is so pervasive.

I’m talking about CamelCased class names with capitalized acronyms.

A camel
A camel. See if you can tell how this animal inspired the term “CamelCase”.

You see these fairly often in Java. EOFException. URLEncoder. ISBNValidator. Mercifully, the good folks who designed the java.net package decided to use Http rather than HTTP in class names, so we’re not cursed with the likes of HTTPURLConnection. Not so with Apple, however, as anyone who’s worked with Objective-C, or even Swift, can attest. The two-to-three-uppercase-letter classname prefix standard (for example, NSThis, NSThat, NSTheOther) is bad enough. But when you couple that with Apple’s overzealous penchant for capitalizing acronyms, you wind up reading AFJSONRequestOperations from your NSHTTPURLResponses.

A strange camel
The weird-ass camel that some language designers have apparently once seen.

SMH.

Though they’re rare, I’ve encountered programmers who voraciously favor the all-caps approach. The rationale is usually that it is, simply, proper grammar. And that’s fair enough. But guess what? It’s also proper grammar to put spaces between words, and to lowercase the first letter of non-proper nouns (unless your nouns are German). Yet we programmers happily break those grammatical decrees on a daily basis.

Instead of being GrammarSticklers, we should be crafting class names to convey the classes’ meanings as clearly and quickly as possible. It’s not that these class names are illegible. It’s simply that they are less legible than they should be. It takes an extra beat or two to understand them. And I don’t know about you, but I don’t like wasting beats.

Am I overreacting? IDK, maybe I am. But thanks for bearing with me anyway. I won’t waste any more of your time.

Collections and Encapsulation in Java Never, ever return null when you're supposed to be returning a Collection.

A core tenet of object oriented programming is encapsulation: callers should not have access to the inner workings of a class. This is something that newer languages such as Kotlin, Swift, and Ceylon have solved well with first-class properties.

Java—having been around for quite awhile—does not have the concept of first-class properties. Instead, the JavaBeans spec was introduced as Java’s method of enforcing encapsulation. Writing JavaBeans means that you need to make your class’s fields private, exposing them only via getter and setter methods.

If you’re like me, you’ve often felt when writing JavaBeans that you were writing a bunch of theoretical boilerplate that rarely served any practical purpose. Most of my JavaBeans have consisted of private fields, and their corresponding getters and setters that do nothing more than, well, get and set those private fields. More than once I’ve been tempted to simply make the fields public and dispense with the getter/setter fanfare, at least until a stern warning from the IDE sent me back, tail between my legs, to the JavaBeans standard.

Recently, though, I’ve realized that encapsulation and the JavaBean/getter/setter pattern is quite useful in a common scenario: Collection-type fields. How so? Let’s fabricate a simple class:

public class MyClass {

    private List<String> myStrings;

}

We have a field—a List of Strings—called myStrings, which is encapsulated in MyClass. Now, we need to provide accessor methods:

public class MyClass {

    private List<String> myStrings;

    public void setMyStrings(List<String> s) {
        this.myStrings = s;
    }

    public List<String> getMyStrings() {
        return this.myStrings;
    }

}

Here we have a properly-encapsulated—if not verbose—class. So we’ve done good, right? Hold that thought.

Optional lessons

Consider the Optional class, introduced in Java 8. If you’ve done much work with Optionals, you’ve probably heard the mantra that you should never return null from a method that returns an Optional. Why? Consider the following contrived example:

public class Foo {

    private String bar;

    public Optional<String> getBar() {
        return (bar == null) ? null : Optional.of(bar);
    }

}

Now clients can use the method thusly:

foo.getBar().ifPresent(log::info);

and risk throwing a NullPointerException. Alternatively, they could perform a null check:

if (foo.getBar() != null) {
    foo.getBar().ifPresent(log::info);
}

Of course, doing that defeats the very purpose of Optionals. In fact, it so defeats the purpose of Optionals that it’s become standard practice that any API that returns Optional will never return a null value.

Back to Collections. Much like an Optional contains either none or one, a Collection contains either none or some. And much like Optionals, there should be no reason to return null Collections (except maybe in rare, specialized cases, of which I can’t currently think of any). Simply return an empty (zero-sized) Collection to indicate the lack of any elements.

Sock drawer
The drawer still exists even when all the socks are removed, right?

It’s for this reason that it’s becoming more common to ensure that methods that return Collection types (including arrays) never return null values, the same as methods that return Optional types. Perhaps you or your organization have already adopted this rule in writing new code. If not, you should. After all, would you (or your clients) rather do this?:

boolean isUnique = personDao.getPersonsByName(name).size() == 1;

Or have your code littered with the likes of this? :

List<Person> persons = personDao.getPersonsByName(name);
boolean isUnique = (persons == null) ? false :persons.size() == 1;

So how does this relate to encapsulation?

Keeping Control of our Collections

Back to our MyClass class. As it is, an instance of MyClass could easily return null from the getMyStrings() method; in fact, a fresh instance would do just that. So, to adhere to our new never-return-a-null-Collection guideline, we need to fix that:

public class MyClass {

    private List<String> myStrings = new ArrayList<>();

    public void setMyStrings(List<String> s) {
        this.myStrings = s;
    }

    public List<String> getMyStrings() {
        return this.myStrings;
    }

}

Problem solved? Not exactly. Any client could call aMyClass.setMyStrings(null), in which case we’re back to square one.

At this point, encapsulation sounds like a practical—rather than solely theoretical—concept. Let’s expand the setMyStrings() method:

public void setMyStrings(List<String> s) {
    if (s == null) {
        this.myStrings.clear();
    } else {
        this.myStrings = s;
    }
}

Now, even when null is passed to the setter, myStrings will retain a valid reference (in the example here, we take null to mean that the elements should be cleared out). And of course, calling aMyClass.getMyStrings() = null will have no effect on aMyClass’ underlying myStrings variable. So are we all done?

Er, well, sort of. We could stop here. But really, there’s more we should do.

Consider that we are replacing our private ArrayList with the List passed to us by the caller. This has two problems: first, we no longer know the exact List implementation used by myStrings. In theory, this shouldn’t be a problem, right? Well, consider this:

myClass.setMyStrings(Collections.unmodifiableList("Heh, gotcha!"));

So if we ever update MyClass such that it attempts to modify the contents of myStrings, bad things can start happening at runtime.

The second problem is that the caller retains a reference to our underlying List. So now, that caller can now directly manipulate our List.

What we should be doing is storing the elements passed to us in the ArrayList to which myStrings was initialized. While we’re at it, let’s really embrace encapsulation. We should be hiding the internals of our class from outside callers. The reality is that callers of our classes shouldn’t care whether there’s an underlying List, or Set, or array, or some runtime dynamic code-generation voodoo, that’s storing the Strings that we pass to it. All they should know is that Strings are being stored somehow. So let’s update the setMyStrings() method thusly:

public void setMyStrings(Collection<String> s) {
    this.myStrings.clear(); 
    if (s != null) { 
        this.myStrings.addAll(s); 
    } 
}

This has the effect of ensuring that myStrings ends up with the same elements contained within the input parameter (or is empty if null is passed), while ensuring that the caller doesn’t have a reference to myStrings.

Now that myStrings‘ reference can’t be changed, let’s just make it a constant:

public class MyClass {
    private final List<String> myStrings = new ArrayList<>();
    ...
}

While we’re at it, we shouldn’t be returning our underlying List via our getter. That too would leave the caller with a direct reference to myStrings. To remedy this, recall the “defensive copy” mantra that Effective Java beat into our heads (or, at least, should have):

public List<String> getMyStrings() {
    // depending on what, exactly, we want to return
    return new ArrayList<>(this.myStrings);  
}

At this point, we have a well-encapsulated class that eliminates the need for null-checking whenever its getter is called. We have, however, taken some control away from our clients. Since they no longer have direct access to our underlying List, they can no longer, say, add or remove individual Strings.

No problem. If we can simply add methods like

public void addString(String s) {
    this.myStrings.add(s);
}

and

public void removeString(String s) { 
    this.myStrings.remove(s); 
}

Might our callers need to add multiple Strings at once to a MyClass instance? That’s fine as well:

public void addStrings(Collection<String> c) {
    if (c != null) {
        this.myStrings.addAll(c);
    }
}

And so on…

public void clearStrings() {
    this.myStrings.clear();
}

public void replaceStrings(Collection<String> c) {
    clearStrings();
    addStrings(c); 
}

Collecting our thoughts

Here, then is what our class might ultimately look like:

public class MyClass {

    private final List<String> myStrings = new ArrayList<>();

    public void setMyStrings(Collection<String> s) {
        this.myStrings.clear(); 
        if (s != null) { 
            this.myStrings.addAll(s); 
        } 
    }

    public List<String> getMyStrings() {
        return new ArrayList<>(this.myStrings);
    }

    public void addString(String s) { 
        this.myStrings.add(s); 
    }

    public void removeString(String s) { 
        this.myStrings.remove(s); 
    }

    // And maybe a few more helpful methods...

}

With this, we’ve achieved a class that:

  • is still basically a POJO that conforms to the JavaBean spec
  • fully encapsulates its private member(s)

and most importantly ensures that its method that returns a Collection always does just that–returns a Collection–and never returns null.

Engineer, promote thyself Increasing your value as a software engineer, two steps at a time

Most of us chose software engineering as a career because we love what we do. Few other fields can reliably give us the satisfaction that writing software does. But there comes a time when you as an engineer will need to decide whether you’ll be content with a career as an individual contributor, or whether you want to advance in your role, taking on more responsibilities and more interesting challenges (and, or course, making more money).

If you plan to stick with simply writing code until you retire, then this article is probably not for you. But if you’re reaching the point where you want to expand your career beyond the individual contributor level, then read on.

Face it: you need to market yourself

Many of us (myself included) have become spoiled by today’s job market. Software engineers–even mediocre ones–tend to be in high demand. But the fact is, plenty of companies can still afford to be picky about who they hire, particularly when it comes to technical leadership roles. In order to compete for the best roles at the best companies, we need to make ourselves more valuable. But being more valuable is not not enough. As they say, perception is everything. So we need to do something that most of us aren’t comfortable with: marketing ourselves. In other words, we need not only to be the most valuable, but also to convince our future employers that we are.

Below are some ideas about how to go beyond simply writing code for a living. All of these ideas have two distinct advantages. First, they show the rest of the world that you know what you’re talking about. That you are thoughtful about what you do, and that you can articulate your knowledge. In other words, they make you appear more valuable.

Second, they force you to sharpen your skills. They ensure that you’ve considered different points of view and are still certain that yours is the best. They help you stretch your skills beyond writing code, improving your research and communication skills. Many of them also open the door to networking with folks who are well-entrenched in the industry. In other words, they make you become more valuable.

Writing


Start a blog

You’ve probably already written a lot about programming. Maybe you’ve contributed to your company’s wiki page. Or you’ve replied to questions on StackOverflow. Maybe you’ve written notes to help yourself remember how to solve a particularly vexing issue. Or you’ve written an email to another engineer explaining a particular programming topic.

There is no reason why you can’t apply those same skills and write some articles. For starters, you can create your own blog. There is some overhead to creating a blog, but not much. If you have already have an account with a cloud provider like AWS, then firing up a new instance to host a WordPress site is trivial. Even easier is to simply set up an account with a site like Blogger. Any time you figure out something interesting, blog about it.

You will of course want to spend a bit more time on a publicly-facing article than you would otherwise. Proofread it more carefully. Let it set for a day or two and re-read it to make sure it makes sense, and edit it as necessary. Maybe have a trusted friend or colleague look it over as well.

Once you’ve posted it, you’ll want to market it. Depending on the topic you write about, you may simply get traffic directly from organic online searches. Beyond that, you’ll want to be sure to that you’re linking to your articles from anywhere else that you have an online presence: Twitter, LinkedIn, etc.

Publish articles on other sites

Once you’ve sharpened your writing skills, you can try publishing your articles on existing online engineering publications. You might think that articles on the likes of JavaWorld and DZone are penned by professional writers, but that’s not the case. In fact, most articles on these sites are written by software engineers like you, and most of these publications allow you to submit articles.

Getting an article published in such a publication is a little (but not much) more work than writing for your own blog. For starters, there’s no guarantee that your writing will be accepted. You’ll want to be even more scrupulous with your writing if you choose this route. Different publications will have different guidelines that you’ll need to follow. Some require you to first submit a proposal before even submitting your writing at all. But they provide a large built-in audience, and carry with them a certain amount of prestige.

Another option is to get yourself involved in engineering communities that interest you. Show enough interest and expertise, and you may be invited to contribute. As an example, I became interested in the Vert.x framework, so I began participating in its discussion forums, and occasionally tweeted about it. After one tweet about how quickly my colleague and I were able to use the framework, I was asked by a Zero Turnaround evangelist–who was also active in the Vert.x community–to write an article about my experience.

Write a book

Depending on your area of expertise, and what ideas you might have, writing a book can also be an option. It’s a time-consuming effort, for sure, but one that can pay off in spades in terms of establishing yourself as a subject matter expert. These days, you have a couple of options:

  • Sell your idea to a publisher like O’Reilley or Manning
  • Self-publish your book
Working with a publisher

Granted, you’ll generally want to start with a novel concept; most publishers will already have authors lined up for How to Program with Java 12, for example. But you’ll be surprised at how easy it is to pitch a niche topic. I was once in a conversation with a publisher from Manning, when he asked me about any topics that I thought would make a good book. As a Java developer who had recently spent time learning Objective-C (this being before Swift came along) I mentioned that Objective-C for Java Developers would have made my life easier. A few days later the publisher contacted me and asked if I’d like to write that book.

While I didn’t wind up writing that book (I did receive a contract from the publisher, but decided to focus my energy on the startup company I’d just joined) in retrospect I often wish that I had. While I’m certain I would’ve made little direct money, I’ve heard from other tech authors about the boosts that they’ve seen in their career. They garner more respect from other people, and in general find it easier to do what they want with their careers, be it getting the job they really want, speaking or publishing more, commanding more money, etc. And along the way, they’ve sharpened their skills and researched their area of expertise far more than they would’ve otherwise.

Working with a publisher has its drawbacks. For example, most publishers have a style in which they want their books written. You’ll work closely with editors, iterating and rewriting. While this will typically result in a much better-written text, it can be time consuming at, at times, frustrating, as you’ll be forced to relinquish much of your creative control to someone else. Typically, while publishers will often offer you a monetary advance as you’re writing your book, you’ll wind up make a small fraction for each book sale.

Self-Publishing

By contrast, it’s become quite easy these days to write and publish your own book. Publishing platforms like LeanPub allow you to focus on writing your book the way you want to write it. They handle the mechanics for you: formatting and publishing, promotion, payments, etc. Most also make it simple to integrate with publishers of physical books (such as Amazon’s CreateSpace). And you’ll be able to keep a much larger percentage of each book sale. Plus, while a publisher can turn your idea down, no one will prevent you from self-publishing.

Of course, when it comes to actually writing the book, you’re on your own. You’ll have no editor guiding you through the writing process (which can be a positive as well as a negative). You’ll be forced to create, and stick with, your own writing schedule (again, this can be a pro or a con). The differences can be summed up like so:

  • Guarantee: First and foremost, self-publishing guarantees that you’ll be able to write about what you want to write about. Established publishers might turn you and your idea down.
  • Prestige: While both options go a long way towards marketing yourself, there is a certain amount of added prestige when working with an established publisher.
  • Control: When self-publishing, you retain control. You write what you want, according to your own schedule. Publishers will insist that you work with an editor, and that you follow their timeline.
  • Monetary: Neither option is likely to make you rich–at least, not directly–so this should not be your primary motivation. With that said, self-publishing allows you to keep a higher percentage of books sold, while working with a publisher generally garners you an up-front advance. Also, while a publisher will keep more money per book, they are likely to be able to increase the overall number of books sold.

Speaking

Speaking is another great way to market yourself and hone your skills. If you haven’t done much speaking before, it can be intimidating to just step in front of hundreds of strangers and start talking. However, there are a number of baby steps that you can take.

Speak at your own company

A great way to build speaking skills and confidence is to start with a small, friendly audience. Assuming you work at a company with other people (engineers or otherwise), giving a technical presentation is a great way to get into public speaking. You first question might be What should I talk about? My advice is to pick from one of the following:

  1. A topic that no one else in the company knows much about, but is important for people to understand.
  2. A topic that other people in the company may be familiar with, but that you in particular excel at

For item #1 above, often you’ll find interesting topics at the at the periphery of programming that have been neglected at your company. Maybe no one knows how to write a good integration test. Or maybe monitoring and tracing might be something your company hasn’t gotten to yet. Take it upon yourself to research and learn the topic, well enough that you can explain it to your colleagues.

For item #2, is there anything for which your colleagues regularly rely on you? This could be anything from git, to specific frameworks, to patterns and best practices. Be sure to outline your talk first. Don’t plan to just wing it. At the same time, don’t fully script your talk. Give yourself permission to ad lib a little bit, and to adjust a bit (say, go deeper into one particular topic, or to pull back on–or even skip entirely–other topics) if you feel that you need to.

Find a meet up to speak at

Engineering meet ups have become extremely common lately. The problem is, lots of engineers want to attend meet ups, but few want to speak. As a result, organizers are often searching for presenters. You can use that to your advantage.

If you haven’t already, find a few good local meet ups to attend (in and of itself, it’s a great way to network and to explore stuff that you don’t typically work with). Get to know the organizers. Then, once you’ve gotten a few talks down at your own company, you can volunteer to present at one of their meet ups. Odds are they’ll be thrilled to take you up on your offer. What should you talk about at a meet up? I’ve been to some great meet ups that have fallen under the following categories:

  1. Deep dives into commonly-used technologies
  2. Introductions to new/emerging technologies
  3. Novel or interesting applications of a given technology

Ordinarily, talks falling under the first category are best given by those most familiar with the technologies. Since they are commonly-used, then almost by definition, the audience will be filled with folks that already know a fair amount about the given technology. For example, I’ve been to a talk about RxJava, given by the team from Netflix that ported the reactiveX framework from .Net to Java. I’ve also attended a talk about Hazelcast… from one of the main Hazelcast developers.

The second category, however, is a different story. If you’ve become familiar with a new technology that has yet to gain widespread use, then you’re uniquely positioned to provide an overview of a technology that few people are using. A year or so ago, for example, I attended an interesting talk about Kotlin given by members of an Android engineering team. The team had no special association with Kotlin itself, other than having adopted the language awhile back. Yet their presentation was well-attended and well-received.

If you’ve made interesting use of a certain technology, then you also have a great opportunity to present. Another interesting talk I’d gone to was given by an engineer whose team had used the Vert.x framework to create a novel in-memory CQRS product.

Conferences

If you’ve attended tech conferences, it can seem as though only engineers who are top in their field are invited to speak. But that’s simply not true. Even more than meet up organizers, conference organizers need to flesh out their conferences with a variety of speakers.

When submitting a conference proposal, however, the topic you plan to discuss becomes very important. While not everyone who speaks is an industry leader, chances are that organizers will be more particular about who presents general topics. Come up with a good niche topic, however, and you’ve got a good chance of being invited to speak. In other words, a talk on What’s new in Spring Framework 6 will likely go to an actual Spring committer. But a talk like, say, Rendering Serverside Graphics Using Websockets and LWJGL, would be open game.

How would you submit a talk? Generally, conferences have a Call For Papers (CFP), which is a period of time in which they are soliciting ideas for sessions. Check the websites of the conferences you want to target for the dates of their CFPs. You’ll find a lot of advice online about how to craft a submission, but common tips include:

  • Submit early in the process
  • Pay attention to your session’s title and make sure it’s both accurate and enticing
  • Don’t pitch a session that attempts to sell a product
  • Be sure you thoroughly understand–and follow–the speaker guidelines

When to use Abstract Classes Abstract classes are overused and misused. But they have a few valid uses.

Abstract classes are a core feature of many object-oriented languages, such as Java. Perhaps for that reason, they tend to be overused and misused. Indeed, discussions abound about the overuse of inheritance in OO languages, and inheritance is core to using abstract classes.
In this article we’ll use some examples of patterns and anti-patterns to illustrate when to use abstract methods, and when not to.
While this article presents the topic from a Java perspective, it is also relevant to most other object-oriented languages, even those without the concept of abstract classes. To that end, let’s quickly define abstract classes. If you already know what abstract classes are, feel free to skip the following section.

Defining Abstract Classes

Technically speaking, an abstract class is a class which cannot be directly instantiated. Instead, it is designed to be extended by concrete classes which can be instantiated. Abstract classes can—and typically do—define one or more abstract methods, which themselves do not contain a body. Instead, concrete subclasses are required to implement the abstract methods.
Let’s fabricate a quick example:
public abstract class Base {

    public void doSomething() {
        System.out.println("Doing something...")
    }

    public abstract void doSomethingElse();

}
Note that doSomething()–a non-abstract method–has implemented a body, while doSomethingElse()–an abstract method–has not. You cannot directly instantiate an instance of Base. Try this, and your compiler will complain:
Base b = new Base();
Instead, you need to subclass Base like so:
public class Sub extends Base {

    public abstract void doSomethingElse() {
        System.out.println("Doin' something else!");
    }

}
Note the required implementation of the doSomethingElse() method.
Not all OO languages have the concept of abstract classes. Of course, even in languages without such support, it’s possible to simply define a class whose purpose is to be subclassed, and define either empty methods, or methods that throw exceptions, as the “abstract” methods that subclasses override.

The Swiss Army Controller

Let’s examine a common abuse of abstract classes that I’ve come across frequently. I’ve been guilty of perpetuating it; you probably have, too. While this anti-pattern can appear nearly anywhere in a code base, I tend to see it quite often in Model-View-Controller (MVC) frameworks at the controller layer. For that reason, I’ve come to call it the Swiss Army Controller.
The anti-pattern is simple: A number of subclasses, related only by where they sit in the technology stack, extend from a common abstract base class. This abstract base class contains any number of shared “utility” methods. The subclasses call the utility methods from their own methods.
Swiss army controllers generally come into existence like this:
  1. Developers start building a web application, using an MVC framework such as Jersey.
  2. Since they are using an MVC framework, they back their first user-oriented webpage with an endpoint method inside a UserController class.
  3. The developers create a second webpage, and therefore add a new endpoint to the controller. One developer notices that both endpoints perform the same bit of logic—say, constructing a URL given a set of parameters—and so moves that logic into a separate constructUrl() method within UserController.
  4. The team begins work on product-oriented pages. The developers create a second controller, ProductController, so as to not cram all of the methods into a single class.
  5. The developers recognize that the new controller might also need to use the constructUrl() method. At the same time, they realize hey! those two classes are controllers! and therefore must be naturally related. So they create an abstract BaseController class, move the constructUrl() into it, and add extends BaseController to the class definition of UserController and ProductController.
  6. This process repeats until BaseController has ten subclasses and 75 shared methods.

Now there are a ton of useful methods for the concrete controllers to use, simply by calling them directly. So what’s the problem?
The first problem is one of design. All of those different controllers are in fact unrelated to each other. They may live at the same layer of our stack, and may perform a similar technical role, but as far as our application is concerned, they serve different purposes. Yet we’ve now locked them to a fairly arbitrary object hierarchy.
The second is more practical. You’ll realize it the first time you need to use one of the 75 shared methods from somewhere other than a controller, and you find yourself instantiating a controller class to do so.
String url = new UserController().constructUrl(key, value);
You’ll have created a trove of useful methods which now require a controller instance to access. Your first thought might be something like, hey, I can just make the method static in the controller, and use it like so:
String url = UserController.constructUrl(key, value);
That’s not much better, and actually, a little worse. Even if you’re not instantiating the controller, you’ve still tied the controller to your other classes. What if you need to use the method in your DAO layer? Your DAO layer should know nothing about your controllers. Worse, in introducing a bunch of static methods, you’ve made testing and mocking much more difficult.
It’s important to emphasize the interaction flow here. In this example, a call is made directly to one of the concrete subclasses’ methods. Then, at some point, this method calls in to one or more of the utility methods in the abstract base class.
In fact, in this example there was never a need for an abstract base controller class. Each shared method should have either been moved to its appropriate service-layer classes (if it takes care of business logic) or to a utility classes (if it provides general, supplementary functionality). Of course, as mentioned above, the utility classes should still be instantiable, and not simply filled with static methods.
Now there is a set of utility methods that is truly reusable by any class that might need them. Furthermore, we can break those methods into related groups. The above diagram depicts a class called UrlUtility which might contain only methods related to creating and parsing URLs. We might also create a class with methods related to string manipulation, another with methods related to our application’s current authenticated user, etc.
Note also that this approach also fits nicely with the composition over inheritance principal.
Inheritance and abstract classes are a powerful construct. As such, numerous examples abound of its misuse, the Swiss Army Controller being a common example. In fact, I’ve found that most typical uses of abstract classes can be considered anti-patterns, and that there are few good uses of abstract classes.

The Template Method

With that said let’s then look at one of the best uses, described by the template method design pattern. I’ve found the template method pattern to be one of the lesser known–but more useful–of the design patterns out there.
You can read about how the patterns works in numerous places. It was originally described in the Gang of Four Design Patterns book; many descriptions can now be found online. Let’s see how it relates to abstract classes, and how it can be applied in the real world.
For consistency, I’ll describe another scenario that uses MVC controllers. In our example, we have an application for which there exist a few different types of users (for now, we’ll define two: employee and admin). When creating a new user of either type, there are minor differences depending on which type of user we are creating. For example, assigning roles needs to be handled differently. Other than that, the process is the same. Furthermore, while we don’t expect an explosion of new user types, we will from time to time be asked to support a new type of user.
In this case, we would want to start with an abstract base class for our controllers. Since the overall process of creating a new user is the same regardless of user type, we can define that process once in our base class. Any details that differ will be relegated to abstract methods that the concrete subclasses will implement:
public abstract class BaseUserController {

    // ... variables, other methods, etc

    @POST
    @Path("/user")
    public UserDto createUser(UserInfo userInfo) {
        UserDto u = userMapper.map(userInfo);
        u.setCreatedDate(Instant.now());
        u.setValidationCode(validationUtil.generatedCode());
        setRoles(u);  // to be implemented in our subclasses
        userDao.save(u);
        mailerUtil.sendInitialEmail(u);
        return u;
    }

    protected abstract void setRoles(UserDto u);

}
Then we need simply to extend BaseUserController once for each user type:
@Path("employee")
public class EmployeeUserController extends BaseUserController {

    protected void setRoles(UserDto u) {
        u.addRole(Role.employee);
    }

}
@Path("admin")
public class AdminUserController extends BaseUserController {

    protected void setRoles(UserDto u) {
        u.addRole(Role.admin);
        if (u.hasSuperUserAccess()) {
            u.addRole(Role.superUser);
        } 
    }

}
Any time we need to support a new user type, we simply create a new subclass of BaseUserController and implement the setRoles() method appropriately.
Let’s contrast the interaction here with the interaction we saw with the swiss army controller.
Using the template method approach, we see that the caller (in this case, the MVC framework itself–responding to a web request–is the caller) invokes a method in the abstract base class, rather than the concrete subclass. This is made clear in the fact that we have made the setRoles() method, which is implemented in the subclasses, protected. In other words, the bulk of the work is defined once, in the abstract base class. Only for the parts of that work that need to be specialized do we create a concrete implementation.

A Rule of Thumb

I like to boil software engineering patterns to simple rules of thumb. While every rule has its exceptions, I find that it’s helpful to be able to quickly gauge whether I’m moving in the right direction with a particular design.
It turns out that there’s good rule of thumb when considering the use of an abstract class. Ask yourself, Will callers of your classes be invoking methods that are implemented in your abstract base class, or methods implemented in your concrete subclasses?
  • If it’s the former–you are intending to expose only methods implemented in your abstract class–odds are that you’ve created a good, maintainable set of classes.
  • If it’s the latter–callers will invoke methods implemented in your subclasses, which in turn will call methods in the abstract class–there’s a good chance that an unmaintainable anti-patten is forming.

Becoming Comfortable with the Uncomfortable Being a strong software engineering professional sometimes has nothing to do with software or engineering at all.

It’s comfortable to complain.

Most of us learned this the moment we were born. As babies, we quickly discovered that crying, loudly, got us what we wanted. A bit later in life, we applied these same lessons for ice cream, TV watching time, and staying up just a little bit later. As teenagers, well, complaining was just the thing to do.

Even as fledgling software engineers, we’ve gotten rewarded for complaining. Pointing out what’s wrong with an organization–its practices, its codebase, etc–initially got us positive attention. We should be using constants instead of hard-coded values in this class? Well done! We’ll assign someone to fix that. Our stand-ups should be moved from 9:45 to 10:00 in case people get in late? Good point. We’ll see about moving them. We should have detailed diagrams of our service architecture on our Wiki? Uh, sure… someone will get to it when they have time.

Because, let’s face it: complaining is easy. But pointing out flaws will only get you so far. At some point, someone needs to address those flaws. And before too long, you may find that your complaints are becoming a liability, and that it’s time to stop grumbling about problems and start doing something about them.

And if you’re like most of us, you’ll pick something comfortable. You write code, after all, so why not find some code to fix? You’ll spend an afternoon refactoring a class to make it more testable. You’ll spend an evening applying the command design pattern to some data access objects. Heck, maybe you’ll spend a weekend coding up a little framework to support your new command-design-pattern-data-access code (and, maybe, someone will actually use it). You’ll show everyone your work, and they’ll tell you what a good job you’ve done. And you’ll feel great, confident that you’ve shown everyone that you’re willing to do whatever it takes to help the organization succeed.

Except for one problem: you haven’t done that all. You’ve shown that you can create fun, interesting coding projects and do them. And before too long, folks will start getting tired of seeing the latest pet project you’d spent your time on while they’d been slogging away at un-fun, uninteresting tasks.

So what’s an enterprising engineer, who truly wants to solve problems and prove their worth, to do?

The answer is easy: find something that’s hard. Something that everyone knows needs to get done, but no one wants to do. Something that’s outside of your comfort zone. Maybe even so far out that you don’t know how to get it done.

In 2015 I joined a company, a Java shop, that was running its applications on Java 7. As I’d been developing on Java 8 for about a year at that point, I was a little disappointed. So naturally I made a glib comment to my new co-workers, and received some knowing grumbles in return. This, or some variation of it, happened for the next few months. Someone would ask in an engineering meeting when we would finally be moving to Java 8. The answer would always be the same: someone has to step up and make it happen.

Finally, one day, I decided that I would be that someone. I knew the task would be tough. I had a full workload, of course, so this effort would be above and beyond what I was hired to do, especially given that I was taking it on voluntarily. There was a lot of risk involved. Even if it went smoothly, we were bound to run into build issues from time to time, and everyone would know who to blame. And in the worst case scenario, I could introduce insidious runtime issues that would reveal themselves early some morning in production.

Plus, I flat out didn’t know how to do it. Installing Java 8 on my own Mac was one thing. But getting the entire organization–its various development, QA, and production environments; monolithic applications and microservices; homegrown libraries and frameworks–all upgraded? I mean, I’m a software architect, not a sys admin!

But I figured the flip side would be that I’d provide a huge service to the organization, modernizing it, and making a number of engineers happy. Besides, I wanted to use streams and lambdas, dammit!

So I announced that I would lead the effort. And it was indeed a large task. I researched the issues commonly encountered by companies doing the same thing. I created a detailed list of dependencies, which drove the order in which applications were to be upgraded. I recruited members of the devops and QA teams, as well as members from individual engineering scrum teams, to be a part of the effort. And indeed, as the project progressed, more than one engineering team ran into vexing build issues.

But most surprising to me was that everybody was fully on board. Despite the effort involved, despite the road bumps encountered, every last person involved was willing to pitch in and help push through. I noticed that the wiki page I’d set up to document common issues and solutions started flourishing with input from other teams. Within a few months, every system in the company was running on Java 8. Years later, I would still receive the occasional word of thanks from engineers in the organization.

Later in the same company, I found myself–along with a number of engineers and engineering managers–complaining about various recruiting and interviewing policies that the engineering organization had adopted. The answer came back: “Someone else needs to come up with better solutions to bringing in good engineering candidates.” I paused for a beat, then agreed: if I’m complaining about it, then I should be willing to find a solution. Again, I didn’t have any particular love for, or skills with, the topic at hand. I certainly was not a recruiter. But it was something that nearly everyone agreed needed to be done. So I assembled a series of agendas, and pulled together small teams to tackle each one. And slowly, we began to improve our recruiting a hiring process.

Since then, I’ve simply taken it for granted that if I identify a legitimate pain point in the organization, it’s my job as a member of that organization to help solve it–whether it has anything to do with software or not. As I spend less time writing code and more time in management and leadership roles, of course, I find myself tasked with a variety of non-engineering issues on a daily basis.

But even if you plan to write code for a living until the day you die (or, more optimistically, retire) taking on non-technical challenges is always a good idea. You’ll boost your own confidence, and your stature within the organization. You’ll get to work with different people, and learn some new things.

And you’ll find yourself with fewer things to complain about.

Using Vert.x to Connect the Browser to a Message Queue

Introduction

Like many of my software-engineering peers, my experience has been rooted in traditional Java web applications, leveraging Java EE or Spring stacks running in a web application container such as Tomcat or Jetty. While this model has served well for most projects I’ve been involved in, recent trends in technology–among them, microservices, reactive UIs and systems, and the so-called Internet of Things–have piqued my interest in alternative stacks and server technologies.

Happily, the Java world has kept up with these trends and offers a number of alternatives. One of these, the Vert.x project, captured my attention a few years ago. Plenty of other articles extol the features of Vert.x, which include its event loop and non-blocking APIs, verticles, event bus, etc. But above all of those, I’ve found in toying around with my own Vert.x projects that the framework is simply easy to use and fun.

So naturally I was excited when one of my co-workers and I recognized a business case that Vert.x was ideally suited for. Our company is a Java and Spring shop that is moving from a monolithic to a microservices architecture. Key to our new architecture is the use of an event log. Services publish their business operations to the event log, and other services consume them and process them as necessary. Since our company’s services are hosted on Amazon Web Services, we’re using Kinesis as our event log implementation.

Our UI team has expressed interest in being able to enable our web UIs to listen for Kinesis events and react to them. I’d recently created a POC that leveraged Vert.x’s web socket implementation, so I joined forces with a co-worker who focused on our Kinesis Java consumer. He spun up a Vert.x project and integrated the consumer code. I then integrated the consumers with Vert.x’s event bus, and created a simple HTML page that, via Javascript and web sockets, also integrated with the event bus. Between the two of us, we had within a couple of hours created an application rendered an HTML page, listened to Kafka, and pushed messages to be displayed in real-time in the browser window.

I’ll show you how we did it. Note that I’ve modified our implementation for the purposes of clarity in this article in the following ways:

  • This article uses RabbitMQ rather than Kinesis. The latter is proprietary to AWS, whereas RabbitMQ widely used and easy to spin up and develop prototypes against. While Kinesis is considered an event log and RabbitMQ a message queue, for our purposes their functionality is the same.
  • I’ve removed superfluous code, combined some classes, and abandoned some best practices (e.g. using constants or properties instead of hard-coded strings) to make the code samples easier to follow.

Other than that and the renaming of some classes and packages, the crux of the work remains the same.

The Task at Hand

First, let’s take a look at the overall architecture:



Figure 1

At the center of the server architecture is RabbitMQ. On the one side of RabbitMQ, we have some random service (represented in the diagram by the grey box labelled Some random service) that publishes messages. For our purposes, we don’t care what this service does, other than the fact that it publishes text messages. On the other side, we have our Vert.x service that consumes messages from RabbitMQ. Meanwhile, a user’s Web browser loads an HTML page served up by our Vert.x app, and then opens a web socket to register itself to receive events from Vert.x.

We care mostly what happens within the purple box, which represents the Vert.x service. In the center, you’ll notice the event bus. The event bus is the “nervous system” of any Vert.x application, and allows separate components within an application to communicate with each other. These components communicate via addresses, which are really just names. As shown in the diagram, we’ll use two addresses: service.ui-message and service.rabbit.

The components that communicate via the event bus can be any type of class, and can be written in one of many different languages (indeed, Vert.x supports mixing Java, Javascript, Ruby, Groovy, and Ceylon in a single application). Generally, however, these components are written as what Vert.x calls verticles, or isolated units of business logic that can be deployed in a controlled manner. Our application employs two verticles: RabbitListenerVerticle and RabbitMessageConverterVerticle. The former registers to consume events from RabbitMQ, passing any message it receives to the event bus at the service.rabbit address. The latter receives messages from the event bus at the service.rabbit address, transforms the messages, and publishes them to the service.ui-message address. Thus, RabbitListenerVerticle‘s sole purpose is to consume messages, while RabbitMessageConverterVerticle‘s purpose is to transform those messages; they do their jobs while communicating with each other–and the rest of the application–via the event bus.

Once the transformed message is pushed to the service.ui-message event bus address, Vert.x’s web socket implementation pushes it up to any web browsers that have subscribed with the service. And really… that’s it!

Let’s look at some code.

Maven Dependencies

We use Maven, and so added these dependencies to the project’s POM file:

<dependency>
  <groupId>io.vertx</groupId>
  <artifactId>vertx-core</artifactId>
  <version>3.3.3</version>
</dependency>
<dependency>
  <groupId>io.vertx</groupId>
  <artifactId>vertx-web</artifactId>
  <version>3.3.3</version>
</dependency>
<dependency>
  <groupId>io.vertx</groupId>
  <artifactId>vertx-web-templ-handlebars</artifactId>
  <version>3.3.3</version>
</dependency>
<dependency>
  <groupId>io.vertx</groupId>
  <artifactId>vertx-rabbitmq-client</artifactId>
  <version>3.3.3</version>
</dependency>

The first dependency, vertx-core, is required for all Vert.x applications. The next, vertx-web, provides functionality for handling HTTP requests. vertx-web-templ-handlebars augments enhances vertx-web with Handlebars template rendering. And vertx-rabbitmq-client provides us with our RabbitMQ consumer.

Setting Up the Web Server

Next, we need an entry point for our application.

package com.taubler.bridge;
import io.vertx.core.AbstractVerticle;

public class Main extends AbstractVerticle {

   @Override
   public void start() {
   }

}

When we run our application, we’ll tell Vert.x that this is the main class to launch (Vert.x requires the main class to be a verticle, so we simply extend AbstractVerticle). On startup, Vert.x will create an instance of this class and call its start() method.

Next we’ll want to create a web server that listens on a specified port. The Vert.x-web add-on uses two main classes for this: HttpServer is the actual web server implementation, while Router is a class that allows requests to be routed. We’ll create both in the start() method. Also, although we don’t strictly need one, we’ll use a templating engine. Vert.x-web offers a number of options; we’ll use Handlebars here.

Let’s see what we have so far:

   
TemplateHandler hbsHandler = TemplateHandler.create(HandlebarsTemplateEngine.create());

   @Override
   public void start() {

       HttpServer server = vertx.createHttpServer();
       Router router = new RouterImpl(vertx);
       router.get("/rsc/*").handler(ctx -> {
           String fn = ctx.request().path().substring(1);
           vertx.fileSystem().exists(fn, b -> {
               if (b.result()) {
                   ctx.response().sendFile(fn);
               } else {
                   System.out.println("Couldn’t find " + fn);
                   ctx.fail(404);
               }
           });
       });

       
       String hbsPath = ".+\.hbs";
       router.getWithRegex(hbsPath).handler(hbsHandler);

       router.get("/").handler(ctx -> {
           ctx.reroute(“/index.hbs”);
       });

       // web socket code will go here

       server.requestHandler(router::accept).listen(8080);

   }

Let’s start with, well, the start() method. Creating the server is a simple one-liner: vertx.createHttpServer()  vertx is an instance of io.vertx.core.Vertx, which is a class that is at the core of much of Vert.x’s functionality. Since our Main class extends AbstractVerticle, it inherits the member protected Vertx vertx.

Next, we’ll configure the server. Most of this work is done via a Router, which maps request paths to Handlers that process them and return the correct response. We first create an instance of RouterImpl, passing our vertx instance. This class provides a number of methods to route requests to their associated Handlers, which process the request and provide a response.

Since we’ll be serving up a number of static Javascript and CSS resources, we’ll create that handler first. The router.get(“/rsc/*”) call matches GET requests whose path starts with /rsc/ As part of Router’s fluent interface, it returns our router instance, allowing us to chain the handler() method to it. We pass that method an instance of io.vertx.core.Handler<io.vertx.ext.web.RoutingContext> in the form of a lambda expression. The lambda will look in the filesystem for the requested resource and, if found, return it, or else return a 404 status code.

Next we’ll create our second handler, to serve up dynamic HTML pages. To do this, we configure the router to route any paths that match the hbsPath regular expression, which essentially matches any paths ending in .hbs, to the io.vertx.ext.web.handler.TemplateHandler instance that we’d created as a class variable. Like our lambda above, this is an instance of Handler<RoutingContext>, but it’s written specifically to leverage a templating engine; in our case, a HandlebarsTemplateEngine. Although not strictly needed for this application, this allows us to generate dynamic HTML based on serverside data.

Last, we configure our router to internally redirect requests for / to /index.hbs, ensuring that our TemplateHandler handles them.

Now that our router is configured, we simply pass a reference to its accept() method to our server’s requestHandler() method, then (using HttpServer’s fluent API) invoke server’s listen() method, telling it to listen on port 8080. We now have a Web server listening on port 8080.

Adding Web Sockets

Next, let’s enable web sockets in our app. You’ll notice a comment in the code above; we’ll replace it with the following:

       SockJSHandlerOptions options = new SockJSHandlerOptions().setHeartbeatInterval(2000);
       SockJSHandler sockJSHandler = SockJSHandler.create(vertx, options);
       BridgeOptions bo = new BridgeOptions()
           .addInboundPermitted(new PermittedOptions().setAddress("/client.register"))
           .addOutboundPermitted(new PermittedOptions().setAddress("service.ui-message"));
       sockJSHandler.bridge(bo, event -> {
           System.out.println("A websocket event occurred: " + event.type() + "; " + event.getRawMessage());
           event.complete(true);
       });
       router.route("/client.register" + "/*").handler(sockJSHandler);

Our web client will use the SockJS Javascript library. Doing this makes integrating with Vert.x dirt simple, since Vert.x-web offers a SockJSHandler that does most of the work for you. The first couple of lines above creates one of those. We first create a SockJSHandlerOptions instance to set our preferences. In our case, we tell our implementation to expect a heartbeat from our web client every 2000 milliseconds; otherwise, Vert.x will close the web socket, assuming that the user has closed or navigated away from the web page. We pass our vertx instance along with our options to create a SockJSHandler, called sockJSHandler. Just like our lambda and hbsHandler above, SockJSHandler implements Handler<RoutingContext>

Next, we configure our bridge options. Here, the term “bridge” refers to the connection between our Vert.x server and the web browser. Messages are sent between the two on addresses, much like messages are passed on addresses along the event bus. Our BridgeOptions instance, then, configures on what addresses the browser can sent messages to the server (via the addInboundPermitted() method) and which the server can send to the browser (vai the addOutboundPermitted() method). In our case, we’re saying that the web browser can send messages to the server via the “/client-register” address, while the server can send messages to the browser on the “service.ui-message” address. We configure sockJSHandler’s bridge options by passing it our BridgeOptions instance, as well as a lambda representing a Handler<Event> that provides useful logging for us. Finally, we attach sockJSHandler to our router, listening for HTTP requests that match “/client.register/*”.

Consuming From the Message Queue

That covers the web server part. Let’s shift to our RabbitMQ consumer. First, we’ll write the code that creates our RabbitMQClient instance. This will be done in a RabbitClientFactory class:

public class RabbitClientFactory {

   public static RabbitClientFactory RABBIT_CLIENT_FACTORY_INSTANCE = new RabbitClientFactory();

   private static RabbitMQClient rabbitClient;
   private RabbitClientFactory() {}

   public RabbitMQClient getRabbitClient(Vertx vertx) {
       if (rabbitClient == null) {
           JsonObject config = new JsonObject();
            config.put("uri", "amqp://dbname:password@cat.rmq.cloudamqp.com/dbname");
            rabbitClient = RabbitMQClient.create(vertx, config);
       }
       return rabbitClient;
   }

}

This code should be pretty self explanatory. The one method, getRabbitClient(), creates an instance of a RabbitMQClient, configured with the AMQP URI, assigns it to a static rabbitClient variable, and returns it. As per the typical factory pattern, a singleton instance is also created.

Next, we’ll get an instance of the client and subscribe it to listen for messages. This will be done in a separate verticle:

public class RabbitListenerVerticle extends AbstractVerticle {

   private static final Logger log = LoggerFactory.getLogger(RabbitListenerVerticle.class);

   @Override
   public void start(Future<Void> fut) throws InterruptedException {

       try {
           RabbitMQClient rClient = RABBIT_CLIENT_FACTORY_INSTANCE.getRabbitClient(vertx);
           rClient.start(sRes -> {
               if (sRes.succeeded()) {
                   rClient.basicConsume("bunny.queue", "service.rabbit", bcRes -> {
                       if (bcRes.succeeded()) {
                           System.out.println("Message received: " + bcRes.result());
                       } else {
                           System.out.println("Message receipt failed: " + bcRes.cause());
                       }
                   });
               } else {
                   System.out.println("Connection failed: " + sRes.cause());
               }
           });

           log.info("RabbitListenerVerticle started");
           fut.complete();

       } catch (Throwable t) {
           log.error("failed to start RabbitListenerVerticle: " + t.getMessage(), t);
           fut.fail(t);
       }
   }
}

As with our Main verticle, we implement the start() method (accepting a Future with which we can report our success or failure with starting this verticle). We use the factory to create an instance of a RabbitMQClient, and start it using its start() method. This method takes a Handler<AsyncResult<Void>> instance which we provide as a lambda. If starting the client succeeds, we call its basicConsume() method to register it as a listener. We pass three arguments to that method. The first, “bunny.queue”, is the name of the RabbitMQ queue that we’ll be consuming from. The second, “service.rabbit”, is the name of the Vert.x event bus address to which any messages received by the client (which will be of type JsonObject) will be sent (see Figure 1 for a refresher on this concept). The last argument is again a Handler<AsyncResult<Void>>; this argument is required, but we don’t really need it, so we simply log success or failure messages.

So at this point, we have a listener that received messages from RabbitMQ and immediately sticks them on the event bus. What happens to the messages then?

Theoretically, we could allow those messages to be sent straight to the web browser to handle. However, I’ve found that it’s best to allow the server to format any data in a manner that’s most easily consumed by the browser. In our sample app, we really only care about printing the text contained within the RabbitMQ message. However, the messages themselves are complex objects. So we need a way to extract the text itself before sending it to the browser.

So, we’ll simply create another verticle to handle this:

public class RabbitMessageConverterVerticle extends AbstractVerticle {

   @Override
   public void start(Future<Void> fut) throws InterruptedException {
       vertx.eventBus().consumer("service.rabbit", msg -> {
           JsonObject m = (JsonObject) msg.body();
           if (m.containsKey("body")) {
               vertx.eventBus().publish("service.ui-message", m.getString("body"));
           }
       });
   }
}

There’s not much to it. Again we extend AbstractVerticle and override its start() method. There, we gain access to the event bus by calling vertx.eventBus(), and listen for messages by calling its consumer() method. The first argument indicates the address we’re listening to; in this case, it’s “service.rabbit”, the same address that our RabbitListenerVerticle publishes to. The second argument is a Handler<Message<Object>>. We provide that as a lambda that receives a Message<Object> instance from the event bus. Since we’re listening to the address that our RabbitListenerVerticle publishes to, we know that the Object contained as the Message’s body will be of type JsonObject. So we cast it as such, find its “body” key (not to be confused with the body of the Message<Object> we just received from the event bus), and publish that to the “service.ui-message” event bus channel.

Deploying the Message Queue Verticles

So we have two new verticles designed to get messages from RabbitMQ to the “service-ui.message” address. Now we need to ensure they are started. So we simply add the following code to our Main class:

   
protected void deployVerticles() {
       deployVerticle(RabbitListenerVerticle.class.getName());
       deployVerticle(RabbitMessageConverterVerticle.class.getName());
   }

   protected void deployVerticle(String className) {
       vertx.deployVerticle(className, res -> {
           if (res.succeeded()) {
               System.out.printf("Deployed %s verticle n", className);
           } else {
               System.out.printf("Error deploying %s verticle:%s n", className, res.cause());
           }
       });
   }

Deploying verticles is done by calling deployVerticle() on our Vertx instance. We provide the name of the class, as well as a Handler<AsyncResult<String>>. We create a deployVerticle() method to encapsulate this, and call it to deploy each of RabbitListenerVerticle and RabbitMessageConverterVerticle from within a deployVerticles() method.

Then we add deployVerticles() to Main’s start() method.

HTML and Javascript

Our serverside implementation is done. Now we just need to create our web client. First, we create a basic HTML page, index.bhs, and add it to a templates/ folder within our web root:

<html>
<head>
 <title>Messages</title>
 <link rel="stylesheet" href="/rsc/css/style.css'>
  <script src="https://code.jquery.com/jquery-3.1.0.min.js" integrity="sha256-cCueBR6CsyA4/9szpPfrX3s49M9vUU5BgtiJj06wt/s=" crossorigin=”anonymous”></script>
  
  <script src="https://cdn.jsdelivr.net/sockjs/0.3.4/sockjs.min.js"></script>
  
  <script src="/rsc/js/vertx-eventbus.js"></script>
  
  <script type="text/javascript” src=”/rsc/js/main.js"></script>
</head>

<body>
 <h1>Messages</h1>
 <div id="messages"></div>
</body>
</html>

We’ll leverage the jQuery and sockjs Javascript libraries, so those scripts are imported. We’ll also import three scripts that we’ve placed in a rsc/js/ folder: main.js and websocket.js, which we’ve created, and vertx-eventbus.js, which we’ve downloaded from the Vert.x site. The other important element is a DIV of id messages. This is where the RabbitMQ messages will be displayed.

Let’s look at our main.js file:

var eventBus = null;

var eventBusOpen = false;

function initWs() {
   eventBus = new EventBus(‘/client.register’);
   eventBus.onopen = function () {
     eventBusOpen = true;
     regForMessages();
   };
   eventBus.onerror = function(err) {
     eventBusOpen = false;
   };
}

function regForMessages() {
   if (eventBusOpen) {
      eventBus.registerHandler('service.ui-message', function (error, message) {
           if (message) {
             console.info('Found message: ' + message);
             var msgList = $("div#messages");
             msgList.html(msgList.html() + "<div>" + message.body + "</div>");
           } else if (error) {
console.error(error);
           }        
       });
   } else {
       console.error("Cannot register for messages; event bus is not open");
   }
}

$( document ).ready(function() {
  initWs();
});

function unregister() {
  reg().subscribe(null);
}

initWs() will be called when the document loads, thanks to jQuery’s document.ready() function. It will open a web socket connection on the /client.register channel (permission for which, as you recall, was explicitly granted by our BridgeOptions class).

Once it successfully opens, regForMessages()is invoked. This function invokes the Javascript representation of the Vert.x event bus, registering to listen on the “service.ui-message” address. Vert.x’s sockjs implementation provides the glue between the web socket address and its event bus address. regForMessages()also takes a callback function that accepts a messages, or an error if something went wrong. As with Vert.x event bus messages, each message received will contain a body, which in our case consists of the text to display. Our callback simply extracts the body and appends it to the messages DIV in our document.

Running the Whole Application

That’s it! Now we just need to run our app. First, of course, we need a RabbitMQ instance. You can either download a copy (https://www.rabbitmq.com/download.html) and run it locally, or use a cloud-provider such as Heroku (https://elements.heroku.com/addons/cloudamqp)  Either way, be sure to create a queue called bunny.queue.

Finally, we’ll launch our Vert.x application. Since Vert.x does not require an app server, it’s easy to start up as a regular Java application. The easiest way is to just run it straight within your IDE. Since I use Eclipse, I’ll provide those steps:

  1. Go to the Run -> Run Configurations menu item
  2. Click the New Run Configurations button near the top left of the dialog
  3. In the right-hand panel, give the run configuration a name such as MSGQ UI Bridge
  4. In the Main tab, ensure that the project is set to the one in which you’d created the project
  5. Also in the Main tab, enter io.vertx.core.Launcher as the Main class.
  6. Switch to the arguments tab and add the following as the Program arguments: run com.taubler.bridge.Main –launcher-class=io.vertx.core.Launcher
  7. If you’re using a cloud provider such as Heroku, you might need to provide a system property representing your Rabbit MQ credentials. Do this in the Environment tab.

Once that’s set up, launch the run configuration. The server is listening on port 8080, so pointing a browser to http://localhost:8080 will load the index page:

blog-message-1.png

Next, go to the RabbitMQ management console’s Queues tab. Under the Publish message header, you’ll find controls allowing you to push a sample message to the queue:

blog-rabbit-1.png

Once you’ve one that, head back to the browser window. Your message will be there waiting for you!

blog-message-2.png

That’s it!

I hope this post has both taught you how to work with message queues and web sockets using Vert.x, and demonstrated how easy and fun working with Vert.x can be.