Last week, I attended JavaZone, the annual conference ran by javaBin, the Norwegian Java User Group. I have attended JavaZone in the past (2018) so I knew what to expect - and the bar was high! Because of that, I was looking forward a lot to join JavaZone again. Also, this would be my first conference as a Java Champion, which made it even more special.
I’ve joined a few sessions that I want to share with you (in chronological order):
- Hitchhiker’s Guide to a Great Developer Career
- Leaving a Legacy (a guide to being kind to those who come after)
- Adressing the transaction challenge in a cloud-native world
- Race between pair programming tools
Hitchhiker’s Guide to a Great Developer Career
What does a developer career look like?
Career moves don’t always give the expected outcome. Wherever you are in your career: hoping - for the best - is not a strategy. So what defines a great developer career?
Many people start as a junior developer. After a few years, you may want to aspire to become a senior developer. A few more years later, new roles appear on your horizon: Engineering Manager, Director of Engineering, maybe even VP of Engineering? They’re leaders, they are in an advisory role… What’s not to like?
But why are we promoting good programmers to become a manager? Or why are we promoting bad programmers to become a manager? Being a programmer requires a different skillset than being a manager!
Let’s face it: a career isn’t always a linear path. Becoming a manager is one possible branch from a career as a developer. Another branch you could take is grow further as an engineer: become a Staff Engineer, Principal Engineer, or Distinguished Engineer. If your organisation has more than roughly 40 engineers, consider defining this path, too!
Or… maybe you want to go for something completely different? IT operations, research department, more customer oriented roles such as sales or support engineer, developer advocacy. Some larger companies offer a Job Rotation program, where you get a chance to try out other roles inside the same company.
Again, whatever your career looks like, it’s seldom a straight line. Usually there all kind of curves, bypaths and turnarounds on the way. Don’t wait for people to come to you and say “now you are a principal engineer”. Envision where you’d love to go and explore the opportuinities there. Make yourself visible to companies or people that can help you land your next job. How? By building a personal brand.
Why a personal brand?
A personal brand is a valuable asset that helps you in standing out from your peers. It’s a way to shine a light on yourself. But it also forces you to think about what drives you, how and why you are different from your peers. In short, it elevates your name, your profile and your values.
So how do you create one? Some practical tips:
- Use the same username everywhere on social media.
- Align your brand: who are you, what change do you envision, what makes you authentic?
- Think about your content: how do you help other people? Could be blogging, but could also be video’s, maybe even TikTok!
- What communities are you a member of? Communities could be great place to network, learn new people, share your own work.
As our industry chances continuously, we need to stay fit and up-to-date in terms of our knowledge.
The classical approach for this would be reading books, or following classroom training. Nothing wrong with that, but know that there are other ways. Consider following a podcast, attending a conference, or contributing to open source.
Or… help your company invest in their people by organising
- “brown bag” sessions, where you can share learnings, have a conversation about it.
- a book club, where you all read a book (completely or in chapters) and discuss what you’ve learned.
- coding session, where you all code in a new framework or language, and share what you did and how you did it.
This way, you can help your company retaining the talent, rather than having them leave after a few years because other companies seem to have more exciting opportunities. Plus it can be a valuable learning experience for yourself to organise these kind of events or gatherings.
Help others grow (or grow yourself)
Seek mentoring, or offer mentoring to others. Mentoring can happen in various fields: side projects, content creation, contributing to open source projects, public speaking… But before you do so, define what your learning goals are in the mentorship relationship. Also think about your time constraints: determine how much time you can spend, and how much time the mentor may have to spend on you. Then write a “mentorship contract”, where the mentee and the mentor both describe their time budgets and constraints, learning goals, etc.
Finally, the speakers discussced a few typical “career stoppers”:
- The lazy one - staying too long in a single position will end your career. This one is definitely busted: if you’re happy with what you do, where you do it, how you do it - it’s good for you!
- The drifter - waiting for the next big opportunity to pass by. This one is plausible: good work doesn’t necesarily speak for itself. Sometimes, you need to boast a bit, maybe even reach out and say “I’m the next one who should get promoted”.
- The lonely wolf - completely neglecting “soft” skills. This one is definitely confirmed: constantly ignoring the role you play in your team or in your company, will sooner or later slow you down or even stop you from growing.
Leaving a Legacy (a guide to being kind to those who come after)
The next talk I would like to highlight is a brand new talk that premiered at JavaZone. This talk is a coproduction between two of my colleagues Tom Cools and Elien Callens. I’ve always been a big fan of Toms style of speaking — if you’ve never seen his talk “Learning through tinkering”, I highly recommend watching it.
So what exactly is a legacy? Something positive that exists even after the person who did it or created it passed away or retired. It’s a situation that exists now because of events, actions etc. that took place in the past.
Examples include Sir Alexander Fleming, who discovered penicillin; Sir Terry Pratchett, who wrote a lot of books that can take you to different worlds; or James Gosling, who was at the roots of Java, the language that unites us here today.
But often, our legacies are negative ones. Projects that struggle to reach a deadline and get into production are then half-heartedly handed over to a maintenance team. And the basis for that is very early in the project.
Joining a project, even if it’s only six months after the start, is a good measure to see how well things have been designed, documented, etc. Your typical first task could be “here’s a simple bug, why don’t you take a look at it?”
Usually, in a greenfield project, there’s a circle from the problem domain, mapping it to a concept, and finally writing to code. But when you later join the team, all of that is implicit: if you’re unlucky, you may only have the code. You can have the same experience when you try to start out contributing to an open source project.
What can you do to improve? Have different onboarding sessions: first discuss the problem and business domain, leaving out the code and nitty-gritty details. Make sure everybody understands what problems the project has been striving to address before diving into the code space. Take into account the different phases of understanding of and thinking about code (see The Programmer’s Brain by Felienne Hermans).
Getting started with a code base can be a daunting task. One of the many difficult tasks would be to setup your development environment. But we live in 2022, and there are modern tools that address this problem, such as That is easily solved with modern tools such as Gitpod, Jetbrains Space or Github Codespaces.
“Situations” we leave behind
1. Undocumented Custom Inhouse Framework
Elien and Tom performed a typical project situation that may occur on an average day-to-day job. After that, so many questions popped up: why didn’t they leave? why didn’t they leave? why didn’t they escalate?
But what did we do? Did we really “keep it stupid simple”? And did we truly adhere to “you ain’t gonna need it”? Rather than pointing at them, we should do the following:
- use established libraries and frameworks (when people know the framework, it’ll be easier for them to understand your code).
- apply apropriate complexity (because people will be able to understand the problem space easier).
- limit the knowledge stack (same as above).
- stick to standards (because it allows people to map unknowns to things they already know).
2. Where are the docs?
An additional observation from the aforementioned performance: there was no documentation. Again, it’s easy to point at them for not bringing this up, not being clever enough…
But rather, we should do the folllowing:
- put the documentation in our repository. This will make it versioned and we can add it to pull requests.
- use Markdown for editing the documentation.
- use a generated website to make it easier to read. Bonus: this also makes it more accessible for business stakeholders.
- not only document what we did, but most importantly, also include why we did it. An Architecture Decision Record is a great way to do this.
3. Where are the bugs?
As the speakers summarised: I like my backlog like my lawn — with bugs in it! Not hidden in a corner, in a “defect bucket” - which often ends up as a defect trash can. It looks good on paper, and managers are happy, but it’s certainly not an honest view.
Rather, we should do the following:
- Keep technical debt and bugs out in the open, instead of hiding them.
- Monitor and improve code quality.
- Use Renovate, Dependabot or a similar tool to keep our dependencies updated.
4. Where are the tests?
To me, legacy code is simply code without tests (freely after Michael C. Feathers). This is definitely not a good approach.
Rather, we should do the following:
- Test our code!
- Test our tests — with mutation testing.
- Make tests as realistic as possible.
5. How to help maintenance succeed
In addition to all of the above, we should do the following:
- Add logging.
- Use ubiquitous language.
- Provide a stakeholder list.
- Add monitoring and alerting.
- Script everything.
- Apply continuous delivery.
Legacy, beyond a project
After all those negative examples, how can we leave a positive legacy behind us? How can we set up others for success?
- Guide: include as many people as you can in a project. Everyone involved will have a bit of knowledge to improve the decision. But even better, they will take their share out of the discussion, and take it with them as they continue to work on the project.
- Build: any kind of tooling that will make operations on the system or on the code base as easy and fool-proof as possible.
- Share: whenever you learn, help others to learn as well. Share what you have learned with others, to spread knowledge. Knowledge is the only thing that multiplies when you share it, so why not share it a bit more?
You might think, isn’t this just all about professionalism? No, this is all about kindness: think about and care for those people that come after you, and how you could make their lives a little bit easier.
All in all, a very interesting, thought-provoking talk that made me leave the room with so many questions about my own behaviour in this. Even though this is a rather extensive summary, there’s a lot left in their talk to experience for yourself. Definitely a recommender if you run into this talk on your conference schedule!
Adressing the transaction challenge in a cloud-native world
The next day, Grace Jansen delivered an introduction to the challenge of transactionality in a distributed environment. The move to microservices and other distributed architectures creates new challenges around data integrity in such architectures. Those “cloud-native” systems should not just barely survive in the cloud, but ideally, they should thrive in the cloud!
The 12 / 15 factor model describes what cloud-native applications should ideally look like. It declares “backing services” as services attached to the application that consumes them. And it describes applications as stateless processes.
But no matter how you look at it, our world itself is stateful. How to overcome this gap?
There are two extremes in terms of application state: Marlin and Dory, both characters from the Finding Nemo animation movie:
- Marlin has a perfect memory. It takes him a long time to decide how to respond to anything he observes. This is because he goes through all his memories, to see if he has previous experiences that may help decide what to do.
- Dory, on the other hand, does not having a memory at all. She responds almost instantaneously to any observation. Because she has no memory, she often runs into problems: she doesn’t recognise that her current observation is potentially dangerous.
Our systems are usually designed in a similar way. Either they are stateful, at the cost of making them a bit slower because they need to access and/or update that state. Or they are stateless, making them fast to response, but not having any memory of what happened before.
The traditional approach for distributed transactions is a two-phased commit (2PC) where all resources needed to perform the work need to be available. Then all of them need to confirm the succesful execution of a request, and only when all of them are succesful, the transaction is really commited. Otherwise, if one of them can’t execute the transaction, all others also need to rollback.
The good part is, this approach conforms to the ACID properties. But on the downside, this can be pretty slow, as the eventual result will only be available when each and everything has been executed and confirmed back to a central coordinator.
An alternative to the above is the so-called Saga pattern. Like 2PC, this approach deals with failure management in a distributed world, aiming to establish consistency in distributed apps. A Saga is essentially a sequence of local transactions, where each transaction updates data within a single service.
A saga consists of a series of such actions with their corresponding compensating actions. Rather than performing a “commit” on a particular action, an action needs to have a compensating action. As the name suggests, a compensating action reverts the original action. This way, the Saga pattern sacrifices consistency and isolation from the ACID properties, but it adds the BASE properties.
So, how does it work? There are two approaches:
Choreography — Each action on a system triggers a follow-up action on a different part of the distributed system. This is often done by firing events, rather than hard-coding the chain of actions to be performed. When a step in a thread of actions fails, this will trigger the previously involved components to perform the compensating actions.
Orchestration — Alternatively, you can implement a saga coordinator which acts as a central coordinator for the actions and their compensating actions. Its job is to keep track of which actions have run successfully, and which actions need compensation because of a failure later on in the process.
Microprofile Long Running Actions (LRA)
Microprofile Long Running Actions (or LRA for short) strives to add first-class support for long-running actions using the Saga pattern. For this, it defines two parties:
- Multiple Participants
Participants can use the following method-level transactions:
@LRA- you’re part of a long running action
@Complete- this methods will be able to
@Compensate- this method will be invoked when a long-running action fails, and a previous action needs to be compensated.
In order to find back previous allocated resources, you can find the long-running action identifier using the HTTP
Storing this value with any resources you create or allocate will allow you to find it back at a later stage.
There’s a lot more to tell about Microprofile LRA and how to use it - maybe that’s interesting for a post on its own.
Race between pair programming tools
To wrap up, I joined Kaya Weers as she discussed four options to do remote pair programming. Pair programming has been around for quite a while. Remote pair programming really caught up during the pandemic, where a physical get-to-gether wasn’t an option. Often, teams would do that using screen sharing tools like Zoom, Teams, etc. but that has its obvious disadvantages.
Pair programming may feel like slowing your team down, but the opposite is often true. It’s more efficient, as we spread our knowledge about the code, our understanding of the domain and the language we use in an early stage. As I’ve learned earlier, this is a great way of being kind to those who come after.
Kaya showcases four tools that are all compatible with Jetbrains’ IntelliJ programming environment.
- Code With Me
All of them - except Code With Me - come as a plugin that will provide a tab in an existing window, or open a new window with the code base of the other person. From there on, you can work together on the same instance of the code base. Code With Me is a little different, as it is in fact a lightweight editor based on the IntelliJ platform. GitLive is also a bit off: it’s not only about pair programming, but instead supports your team in all aspects of the remote co-working, including for example issue management.
Kaya then continues to score each contestant on four axis:
- Coding environment
- Access to session
- Pair programming options
Also, there are two special fields where the four candidates can score additional points:
- own IDE - are you able to use your own programming environment?
- continuous collaboration - does the tool support you in the whole range of remote collaboration, rather than only the shared programming session?
I won’t spoil the outcome of the race - for that, you’ll have to join the talk yourself.
I’m looking back at a couple of intense and interesting days. It was great to reunite with old friends, and meet new ones. Just like 2018, the first year I attended JavaZone, the crew did an amazing job of creating a great event. It’s hard to believe that most of the event is ran by volunteers. Definitely a hat-tip to everyone involved!