Our favourite talks from Krk.rb 🇵🇱

Baptiste Acca
Inside Aircall
Published in
6 min readJun 11, 2019
Browar Lubicz — The brewery where the event was hosted

Earlier this month, we had the chance to attend Krk.rb, a 2-day Ruby conference in Kraków 🇵🇱 organized by KRUG (Kraków Ruby Users Group). The first day was filled with talks from a lot of talented speakers and the second day was dedicated to workshops with some of the speakers. It was the first edition of Krk.rb and most likely not the last considering the success it had. Here are the conferences we most liked from the event.

This article is co-authored by Christophe Valentin and Baptiste Acca

Moving beyond #call — Demystifying functional Ruby

Piotr Solnica, ROM.rb creator and dry-rb core team member

TL;DR: Piotr showcased ways to make ruby more functional and the advantages of doing it: lighter code, easier to understand and maintain.

Piotr Solnica, better known as solnic, is a very active member in the open source ruby community, having created ROM.rb and being a core team member of dry-rb. He presented us his view of what good functional Ruby is.

There is an eternal debate about Ruby being a functional language or an object-oriented language. Turns out, it’s up to you! Ruby provides object-oriented tools like stateful classes and also functional programming oriented tools like lambdas or procs.

With this in mind, it’s important to be consistent. When aiming for functional Ruby, you should follow some simple principles.

Reduced mutability

Immutability is not built in Ruby and freezing all your variables is quite costly. Instead, you can aim for reduced mutability:

  • In your objects, don’t implement public api methods that mutate state: always return a new instance with updated attributes. That way, you don’t need to worry about side effects happening with the mutated initial object.
  • Do not mutate your objects (strings, arrays, hashes). Learn to avoid the methods that mutate data-structures: use gsub instead of gsub!, map instead of map!, etc.
  • Don’t rely on global variables: they can be mutated and you won’t be aware of it. For example, i18n relies on mutable data which can become problematic.

Conventions

  • The class name of your services / use-cases should be a verb. That way, it reads closer to a function. For example, instead of naming the service in charge of creating your users UserCreator, aim for something like User::Create. This can surprise regular Rails developers but is the norm in Domain Driven Designed projects.
  • Make your services / use-cases as stateless as possible. The initialize method should be used to setup collaborators, like your repositories or other services that will be used. It should not hold data that would change the behaviour of the service / use-case.
  • Use the same method name to run your services / use-cases: #call. That way, all your code is consistent, and you don't have to guess the name of the primary interface of each service / use-case. In addition, #call is also the method used by procs, which makes it even more consistent.
  • Everything is about data processing flow. You pass an input, it is processed in several steps and provides an output.

Impacts on design and maintainability

When applying these principles, you can achieve real code reusability. Since every service / use-case has a single role and is stateless, you can use them from everywhere in your codebase, which makes it really easy to re-use and reduce the amount of code your project holds. Less code = less bugs.

It makes your project more straight forward. Your business logic business logic is a succession of readable function calls describing its actions, which makes it more readable and therefore more understandable. There is no business logic hidden behind callbacks or magic methods, which makes your code more explicit, easier to extend and maintain.

It can also lead to better performance. By avoiding callbacks and side effects, you’ll end up with simpler code that is often faster.

Domain events & Kafka in Ruby applications

Spyros Livathinos, Software Engineer @ Zendesk

TL;DR: Spyros presented interesting patterns for saving data in a MySQL DB and sending events in Kafka at the same time, while taking advantage of DB transactions.

Because they are critical to the business logic of services, domain events need to be reliable. When introducing a new domain event, you always have to think about how this event will be consumed downstream. Is loosing one click event OK? It’s just OK, isn’t it? Well not quite, if your billing system is using that event to bill clients!

Let’s imagine the following use case: my users can edit a setting in their account. When they save that setting, it updates the value in a MySQL DB, and sends an event in Kafka. What will happen if the DB succeeds but the publishing on Kafka fails? Because the two systems are independent, we need to find a solution that maintains consistency.

Spyros presented a quite unusual solution, combining the 2 following concepts:

  • The Outbox pattern + Change Data Capture
  • Blackhole engine

Outbox pattern + Change Data Capture (CDC)

This solution is inspired by mail clients’ outbox. When we persist our setting in DB, we also persist the event we want to send on Kafka in an outbox table. That way, we can take advantage of transactions: failing to save the setting or the event will result in the whole operation being rolled-back. Additionally, we setup our DB to use binlogs, which are files containing events of changes made to the DB. Finally, we make a service (binlog relay) watch the generated binlog file and send events to Kafka.

That solution looks promising but comes with a caveat, we introduced a new table that is bound to becoming huge as our service ages. What if we could use that pattern without increasing the size of our DB?

Black hole engine

As the name suggests, the black hole engine ingests data but nothing is persisted in the DB. The cool part is that you can write regular queries but no actual rows are created. And as a bonus, the events are logged in the binlog. So by combining this feature with the Outbox pattern, you can get a system that:

  • is transactional
  • does not bloat the DB
  • disconnects application logic from message bus, since it’s the binlog relay’s responsibility to send the events on Kafka

Honourable mention

Manipulating Ruby Abstract Syntax Tree — Jônatas Paganini.

A live coding presentation that blew our minds! Jônatas wrote a wrapper around the AST to easily traverse it. Use case? Writing a new Rubocop cop in less than 15 minutes!

Main takeaways

Krakow’s Ruby community is vibrant and composed of pretty good senior engineers!

All the talks on domain events warned the audience about the importance of thinking long and hard about the events’ schema evolution. Making changes to your events makes replay and consumption by multiple services hard. There are ways to mitigate the risk by using message formats embedding the schema definition (eg. Apache’s avro or Google’s protobuf).

Using Ruby as a functional language makes it really easy to maintain and understand. It looks like the way to go, especially when working with micro-services. By following conventions, you make your code consistent. It’s also easier to onboard new people on your project!

A strong emphasis was made on the fact that open source software is done primarily to answer the needs of the person that builds it. This means, it is really important for everyone and especially companies, to contribute to it. Being a recognised open-source involved company can help improve your hiring, have a better brand recognition and ultimately build tools best tailored to your needs. So don’t hesitate, get involved now!

Huge thanks to Maciej Mensfeld for organising the event, it was great! Hope to be back next year :)

PS: Want to join our great team? We’re hiring! Apply now on https://aircall.io/jobs/

Follow us — @aircalltech

--

--

Baptiste Acca
Inside Aircall

Software Engineer @aircall, I like cats, junk food and tv shows