Dunja Radulov wrote this on January 24, 2017

Semaphore Winter '16 Hackathon

The end of each year is a time to make plans for the following year and get inspired to work on new and exciting things in the year ahead. In December, we decided to organize a Semaphore hackathon for the entire company to celebrate the end of 2016 by getting creative.

People pitched their ideas and others voted with their feet by joining the projects they were most interested in. We split into multidisciplinary teams that had three days to capture new ideas and have fun making them a reality. Keep reading to find out what are the projects we came up with, what we learned working on them, and what are some of the outcomes of our first internal hackathon.

Semaphore 2016 hackathon at Rendered Text

Improving the Semaphore SSH session

Team members: Ervin, Igor, Marko, Milan, Milica and Sneha

The Semaphore SSH session proved to be a popular asset for debugging builds and deployments. From the start, there were things we wanted to improve, but the team was short on time, and these changes weren’t very high on our list of priorities, so our team decided to work on some improvements during the hackathon.

The most obvious thing we wanted to improve was the lack of contextual information, e.g. the build which is currently checked out in the session and time until completion. We decided to add a few extra touches which would provide this information right off the bat. Another pain point we wanted to address is copy-pasting the build commands into the SSH environment. The utility addressing this shortcoming is soon to be released.

Improved SSH session on Semaphore

We played around with a few other ideas which didn’t see the daylight, but the hackathon was a great team effort and bonding experience for everyone in the team.

Finding trends in support request messages

Team members: Milana, Marija, Filip, Nemanja and Jovan

Our team was interested in finding trends in support request messages sent by our users. For this purpose, we collected a set of our messages from Intercom, and applied various machine learning techniques to it.

We spent our hackathon time playing around with the messages, clustering them based on topic using different algorithms and trying out sentiment analysis on them. We mostly relied on Python and its rich machine learning libraries as our tools.

After three days, we ended up with a prototype which tracks the rate of occurrence of current topics and visualizes this with Kibana. The hackathon provided us with a wonderful opportunity to step outside of our everyday tasks and tools and develop ideas that otherwise might not have been considered worth our time. Combining this with a mini-vacation-like atmosphere, the entire event proved to be refreshing, fun, and a great prelude to the upcoming holiday season.

Making the TV great again

Team members: Marko, Nemanja, Nikola, Stefan and Misel

Our team looked around the office and asked ourselves, what is there that we can improve and make great again? As it often turns out, you don’t need to look far. There was a spare TV sitting turned off in the office lobby, so we decided to make it show a dashboard that would be fun and useful for everyone.

The Semaphore TV now shows things like a photo from the place our latest customer hails from, recent tweets to @semaphoreci, and a world map of recently active users. Best of all, it plays a different music theme every time we get a new customer.

On the tech side, we played with serverless architecture and implemented the dashboard and all supporting endpoints as microservices running on AWS Lambda. Recent support for running Express.js apps on Lambda has been very useful. Overall, it was a really fun experience!

Improving employee experience

Team members: Katarina, Milica, Filip and Marija

Our team had many ideas simming in their heads on what would help improve our employee experience, so we decided to work on two different projects.

We wanted to explore different employee engagement tools for a while now, so the hackathon presented the right opportunity to do so. For the first project, we created a new Slack channel named ‘Humans and Bots’, and picked three tools seemed most promising. We decided to test drive Leo from the Office Vibe, Polly chat bot and Captain Feedback. Each tool has different merits. We found the Polly to be the most engaging one. Polly is currently a free tool, and it’s easy to use straight from the start. It has predefined questions on ‘Team Happiness’, and you can choose the frequency of questions asked, as well as set your answers to be completely anonymous if you wish so.

The second project was to create a simple static site for our internal use with interesting links for the team and a Google form questionnaire on team happiness. This was a fun project to do, and it included some coding. The content included were useful slideshares, interesting books to read, and links to events to attend. All in all, the hackathon was a great learning experience and a nice opportunity for teamwork.

The path to TDD enlightenment

Team members: Nemanja, Tamara, Milica and Dunja

Since we had a multidisciplinary team including a developer, a designer and two marketers/editors/writers, we decided to use our combined skills to create an interactive experience (i.e. a game) that plays with the pitfalls of software development and the true path of TDD.

The Path to TDD enlightenment game

The game puts the player in the shoes of a developer chosen to lead a team cleaning up a tricky project, an app left behind by a developer that’s rumoured to have gone insane. The code is complex, unreadable and ridden with bugs, and the deadline is tight, so the player needs to use TDD and common sense in order to manage the chaos without spilling too many tears. Working on the game helped us remember and better understand why testing and TDD matter, and allowed us to get creative and have fun together as a team.

All in all, the hackathon was a fun experience during which we played with some new ideas, bonded as a team, and got energized for a creative and fun year ahead of us.

Marko Anastasov wrote this on September 27, 2016

Petar Perovic joins Rendered Text

We’re super excited to announce that Petar Perović has joined the Semaphore product team!

Petar Perovic

Petar is an experienced UX designer and retired drummer. We also have a bit of a personal history together: back when Rendered Text was a small consulting shop and he was with Superawesome, we used to share an office, and we worked on a website together. Back then, he was schooling us at the foosball table, and he designed our very first website and identity. Five years later, we’re rejoining forces to work on making the nicest CI experience out there.

Petar is working with us remotely from Porto, Portugal, where he lives with his wife and no dogs.

Be sure to follow Petar on Twitter. Welcome, Petar!

Interested in joining our team? Check out our jobs page to see what we’re working on and get in touch, we’d love to hear from you.

Dunja Radulov wrote this on September 2, 2016

Meet Predrag Rakic

This week, we are continuing our series of posts on our team members working on Semaphore. It’s our pleasure to introduce you to Predrag Rakić, who joined our team this year as a Software Development Engineer.

Predrag Rakic

Predrag started his career in academia — he received his PhD degree in Computer Science from the Faculty in Technical Sciences in Novi Sad, where he worked as a Researcher and Assistant Professor. In 2013, Predrag set out on a new adventure and joined Amazon’s dev team. He worked on the fastest-growing AWS project, Aurora storage engine, a high-performance, log-record based, distributed, quorum-based database cloud storage service.

The idea of being part of a small team of passionate developers who are dedicated to creating a software product used worldwide attracted Predrag to join Rendered Text. He’s currently working on improving Semaphore’s job logs system, as well as setting up an internal metrics and alerting service based on StatsD, InfluxDB, and Grafana.

When he’s not playing with metrics and code, Predrag can be found somewhere in nature, preferably in a boat on the Danube, relaxing and watching the river flow.

Interested in joining our team? Check out our jobs page to see what we’re working on and get in touch, we’d love to hear from you.

Dunja Radulov wrote this on August 11, 2016

Welcoming Milica Maksimovic to Our Team

We’re happy to welcome Milica Maksimovic, our new Community Editor, to the Rendered Text team. If you’re a Semaphore Community reader or writer, you might have been corresponding with Milica over the past few months.

Milica Maksimovic

Milica loves learning about code and hanging out with programmers, but she’s not quite geeky enough to become one of them. This is why she is the perfect person to work on developing our community. Outside work, Milica is passionate about open source, women’s rights, and personal development. She loves playing with Fedora, reading fiction, playing video games, and riding her bike named Juliet.

Even though Milica joined our team only recently, we already couldn’t imagine our team without her.

Welcome Milica!

P.S. You can follow Milica on Twitter @m_maksimovic_.

Dunja Radulov wrote this on July 1, 2016

Rails Girls Novi Sad #4: Empowering Women Through Code

Last weekend, we co-hosted the fourth Rails Girls workshop, welcoming 20 women to our office to learn how to develop web applications.

Rails Girls is a global initiative with the aim of empowering women through teaching them how to code in Ruby on Rails.

The two-day workshop is quite challenging, but all the participants did a great job and successfully learned how to design, prototype and code their first web applications.

Rails Girls Novi Sad at Rendered Text

Our friends from the organizing team — Milana Ljubisavljević, Aleksandra Vukošić and Kristina Stojaković have been joined by Smiljana Mihajlović, who is now working on promoting the initiative. As always, they all did a great job organizing the workshop.

Rendered Text had two first-time volunteer mentors in this workshop - Ervin Barta and Jovan Ivanović. We’re proud that the majority of our developers have now participated as mentors in the workshops :)

Rails Girls Novi Sad Workshop

To make this blog post a more interesting read, this time around we decided to chase our beloved mentors around the office and ask them to write a few words about the workshop. Ervin enjoyed participating in the workshop:

Since the best way to learn is to teach, I decided to take part in the workshop as a mentor for the first time. I think it’s important to bring programming closer to people, because the majority is afraid of its complexity. However, with modern tools at hand, and after spending a little time behind the keyboard, they get amazed at how much they’ve accomplished in such a short amount of time. This Rails Girls event was a great opportunity to put this into practice, and I’m happy that I got the chance to take part in it. We had lots of fun, made new friends and learned something new along the way.

Rails Girls Novi Sad Workshop

Here are Jovan’s impressions about the workshop:

Rails Girls is an excellent opportunity to help reduce the underrepresentation of women in programming by showing the attendees how to take their first steps the Ruby world. This will hopefully serve as a starting point from which they will expand their knowledge, and continue creating their own applications. The importance of workshops like this one is in changing the way we see programming from an esoteric activity for the select few to an activity that is transparent and open to all.

We’re happy to support a great initiative like this one, and we’re looking forward to meeting more future girl programmers in the workshops to follow.

Rails Girls Novi Sad Workshop

Take a look at our Rails Girls Facebook album to see more photos from the workshop.

If you’d like to participate in the next workshop, you can follow @RailsGirlsNS and @RenderedText on Twitter, or subscribe to our events RSS feed.

Darko Fabijan wrote this on May 12, 2016

Welcome Sneha

We’re pleased to announce that Sneha Somwanshi has joined Rendered Text.

As our first remote employee, she will help us break new ground in how we work. She’s bringing valuable experience from both Ruby on Rails, and DevOps worlds. Prior to joining Rendered Text, she worked at ThoughtWorks.

Sneha loves traveling, conquering mountain tops, and shares our excitement in adopting Elixir and micro-services.

Welcome Sneha! Follow her on Twitter at @snehasomwanshi.

Igor Šarčević wrote this on April 7, 2016

Ecto for Rails developers — The basics

We at Rendered Text are huge fans of Ruby on Rails. As a proof, SemaphoreCI — our biggest product — is still mostly written in Ruby. Lately, however, we started migrating toward Elixir and the OTP platform. Erlang has an excellent platform for developing giant, scalable and fault tolerant systems. Elixir, on the other hand, is a Ruby-like language that helped us to swiftly transition parts of our infrastructure, and harness the power of OTP by using a familiar syntax.

We noticed two big differences right from the start.

  1. Elixir is a mostly pure functional programming language.

  2. Ecto — the de facto way to talk to databases in Elixir — has a vastly different approach than ActiveRecord.

The first issue was easy to overcome. We are huge fans of functional programming. Several of us has a working knowledge of Lisp or Haskell, and we even aim to keep our Ruby code immutable and functional. The differences in the database abstraction was a bit harder for us. We were used to Active Record quite a lot. This article is dedicated to this issue, and it is my best attempt to help you transition easier.

Ecto is NOT an ORM

The first big difference you will notice is that Ecto doesn’t convert your database rows into objects. Instead, it uses a database wrapper mechanism, called Repository, and pure Elixir data structures to return your data. Let’s see an example. In Rails, to fetch all the users who are older than 18 would probably look like the following:

users = User.where("age > ?", 18)

# users are instances of the User class
user = users.first

p user     # => #<User id: 1, age: 20>
p user.age # => 20

# we can update the object and save it back into the database
user.age = 25

In Elixir, you don’t have objects, only pure data. All the communication must be passed through the Repo.

users = User |> where([u], u.age > 18) |> Repo.all

# users are hashes. `hd` is short for head of the list
user = hd(users)

IO.inspect user   # => %User{age: 20, id: 1}
IO.puts user.age  # => 20

# first we create a changeset
changeset = User.changeset(user, %{age: 25})

# then we send the changes into the repository
changeset |> Repo.update

As you can see, even though Ecto is not an ORM, it does resemble it.

The separation of the wrapper — the Repository — from the query interface was probably the most interesting thing when I was switching to Elixir.

Let’s start our Ecto journey with some basics. The rest of the article will teach you how to invoke CRUD operations on your repository.

Creating new entries

Creating new rows in the database is achieved by constructing a structure and sending it to the repository.

{:ok, user} = %User{name: "Igor", age: 25} |> Repo.insert

Like in Rails, we have two formats for the insert function. The regular version shown in the previous example that returns either :ok or :error, and a bang version Repo.insert! that raises an exception.

user = %User{name: "Igor", age: 25} |> Repo.insert!

Reading values from the database

To verify that our insert action succeeded, we will try to fetch it by using its id:

# id of the record created in the previous example
id = user.id

igor = User |> Repo.get(id)

The get actions can return either nil if the record is not present in the database, or a structure representing your record.

if igor |> is_nil do
  IO.puts "User with id: #{id} not found"
  IO.puts "User with id: #{id} is present"

Updating rows in the database

Unlike in Rails where we would modify the returned object, in Elixir, we will use changesets. A changeset represents some changes that we want to send to our repository. For example, let’s make our user a bit older than he is:

# first we record the changes
changeset = user |> User.changeset(%{age: 30, name: "shiroyasha"})

# then we send the changes into the repository
changeset |> Repo.update

When you get comfortable with Elixir you will most likely write the above in only one line:

user |> User.changeset(%{age: 30, name: "shiroyasha"}) |> Repo.update

The changesets are also pipable, so it is easy to make two changes separately.

|> User.changeset(%{age: 30})
|> User.changeset(%{name: "shiroyasha"})
|> Repo.update

Deleting records from the database

Finally, let’s delete the user from our database:

{:ok, _} = user |> Repo.delete

Final words

I hope that this article helped you to get started with Ecto. Here are some useful resources to continue your learning process:

Happy hacking in Elixir!

Marko Anastasov wrote this on April 7, 2016

Katarina Ugrinić joins Rendered Text

Today we’re reviving the custom of announcing new team members on the blog. These are exciting times for Rendered Text as we’ve been setting up various functions in the company that have not had dedicated people before. We’re very happy to see these fantastic people come and do great work. As a result, we’re collectively able to accomplish awesome things that were not possible before.

Katarina Ugrinić has joined us in our office in Novi Sad as our first recruiting manager. With her background in business finance and years of experience working in HR for tech companies in UK, she’ll be helping us find and onboard more amazing colleagues while keeping the team dynamics healthy. Katarina is a mom of three, likes roller skates and enjoys going out exploring the beauties of Serbia. In her own words:

Working with people is really stimulating and I really enjoy it. I am passionate about selecting the best person, who will not just fit the skills set, but most importantly shares our company’s values and personality traits. I like to see people growing and developing their full potential. I am excited to be joining Rendered Text through the expansion. I’m looking forward to see us growing and of course all the employees happy!

Welcome Katarina! Follow her on Twitter at @KatarinaUHR.

Igor Šarčević wrote this on February 18, 2016

Inject is a fundamental building block

Inject is one of the fundamental, and most versatile constructs available in functional languages. It can be used to implement map, select, max, all? and a bunch of other iteration related methods. Unfortunately, many programmers are not aware of its awesome powers. This article is here to improve this fact.

Let’s dig in.

Simple array iteration

First, let’s start with an example problem — finding the sum of numbers in an array:

def sum(numbers)
  result = 0

  numbers.each do |number|
    sum += number


sum([1, 3, 5, 7, 9]) # => 25

Now, let’s try to solve the same problem with a simple limitation. Let’s calculate the result without changing the values of the variables. In other words, we will try to avoid explicit state changes like in the following example:

sum += number

This is a very important step to make our code more functional. Changing the values of variables, in other words making side effects, is preventing our code to run on multiple processors effortlessly. Side effects are also a common thing that can introduce hard to find bugs.

In the functional world, instead of a for-each iteration, programmers use recursion. Let’s rewrite the above code segment with a recursive implementation:

def sum(numbers)
  if numbers.empty?
    first, *rest = numbers

    first + sum(rest)

sum([1, 3, 5, 7, 9]) # => 25

It can be a little hard to grasp the idea if this is the first time you are encountering recursion in your life. The idea behind it is, however, very simple. After several tries and errors it can be mastered easily.


In order to create a more general purpose algorithm, and to optimize it for tail calls we will allow our sum method to accept a starting value. We will call it accumulator.

def sum(accumulator, numbers)
  if numbers.empty?
    first, *rest = numbers

    sum(accumulator + first, rest)

sum(20, [1, 3, 5, 7, 9]) # => 45

The name ‘accumulator’ can be confusing, but you can think of it as a variable that accumulates the result. Its purpose is equivalent to the result variable in our original imperative implementation.

Introducing inject

Now it is safe to introduce the inject method — an abstraction for the above recursive construct. Let’s use it to sum numbers:

[1, 3, 5, 7, 9].inject(20) { |accumulator, number| accumulator + value } # => 45

By renaming the variables we can make the above line more straightforward:

[1, 3, 5, 7, 9].inject(20) { |result, number| result + value } # => 45

Let’s use it to calculate the product of an array:

[1, 3, 5, 7, 9].inject(2) { |result, number| result * number } # => 805

The above patters are very handy when we want to convert an array of values into one value. This is one of the main strengths of the inject method. In this example we are reducing the array into a single value. This is why the inject method is commonly also named reduce.

Less verbose injecting

If you think about it, the { |result, number| result + value } is repeated for both of the above examples. Luckily, Ruby is a powerful language that enables us to write the above lines even shorter. The :* is a shorthand value for a block that multiplies its arguments. Let’s use it:

[1, 3, 5, 7, 9].inject(1, :*) # => 945

This representation can give us a deeper insight into the name of the inject method. We can think of inject as a mechanism that injects a * operator between the elements of the array.

1 * 3 * 5 * 7 * 9

Implementing sum and product

Let’s implement the sum and product methods using inject:

def sum(elements, from = 0)
  elements.inject(from, :+)
def product(elements, from = 1)
  elements.inject(from, :*)

Implementing map

The above example is nice, but it is not nice enough to be called a fundamental iteration block for functional programmers. Luckily, inject can do much more. The map method can be tough about as a special kind of inject. The following two code block are equivalent:

[1, 2, 3].map { |el| el * el }
[1, 2, 3].inject([]) { |result, el| result + [el * el] }

We can even implement a map method by using inject:

def map(elements, &block)
  elements.inject([]) { |result, el| result + [block.call(el)] }

Implementing select

We can even implement a select method using inject:

def select(elements, &block)
  elements.inject([]) { |result, el| result + (block.call(el) ? [el] : []) }

We can reuse the previous definition of map and sum to make it shorter:

def select(elements, &block)
  sum(map(elements) { block.call(el) ? [el] : [] })

Even the reject method is simple:

def reject(elements, &block)
  elements - select(elements, &block)

Implementing min and max

We can implement a min method that returns the smallest element in the array. The trick is to store the current minimum as the accumulated value:

def min(elements, &block)
  elements.inject(Float::INFINITY) do |minimum, el|
    el < minimum ? el : minimum

Symmetrically, the maximum value can be calculated:

def max(elements, &block)
  elements.inject(-Float::INFINITY) do |maximum, el|
    el > minimum ? el : maximum

Implementing all?

This is my last example, and hopefully you will be convinced that almost every method in Ruby’s Enumerable module can be implemented as an special case of the inject function.

Let’s construct the all? method that checks if every element in the array satisfies a given check block:

def all?(elements, &block)
  elements.inject(true) { |result, el| result && block.call(el) }

Similarly, the any? method can be implemented:

def any?(elements, &block)
  elements.inject(false) { |result, el| result || block.call(el) }

Should I use inject to write code?


Many programmers that learn the inject method start to use it all over the place. While I admit that inject is a truly powerful construct, it is not something that should be used everywhere, especially not in business level logic.

Instead of using inject everywhere, use it to construct new, domain level functions that you can use in you code. For example, instead of writing:

usernames = ["tim", "jake", "jennifer", "marcus"]

usernames.inject({}) do |result, username|
  result.merge(username => username.length)

# => { "tim" => 3, "jake" => 4, "jennifer" => 8, "marcus" => 6 }

I encourage you to write a specific method that creates a hash from the input and output array. Let’s call this method hashmap:

def hash_map(elements, &block)
  elements.inject({}) do |map, el|
    map.merge(el => block.call(el))

Then use it to calculate the same value simpler:

usernames = ["tim", "jake", "jennifer", "marcus"]

hash_map(usernames) { |username| username.length }

# => { "tim" => 3, "jake" => 4, "jennifer" => 8, "marcus" => 6 }

Or even shorter:

hash_map(usernames, &:length)

If you feel ambitious, you can even add it as a method to Array:

def Array
  def hash_map(&block)
    self.inject({}) do |map, el|
      map.merge(el => block.call(el))

Final words

Inject is super awesome, and powerful enough to express most of the iteration logic in functional languages. However, never forget the Spiderman hypothesis:

With great power comes great responsibility.

Happy injecting!

Dunja Radulov wrote this on December 16, 2015

Rails Girls Novi Sad #3 at Rendered Text

On 5 and 6 December we had the pleasure of welcoming the third Rails Girls workshop at our office.

Rails Girls is a global volunteer community with the goal to give women the opportunity to learn how to code in Ruby on Rails, and use that knowledge to build their own applications.

Rails Girls Novi Sad at Rendered Text

The third Rails Girls workshop in Novi Sad was organised by Kristina Stojaković, Aleksandra Vukošić and Milana Ljubisavljević, who have done a wonderful job of getting together a great team of mentors, coordinating the workshop, and creating a perfect opportunity for women interested in programming to take their first steps in the world of Ruby on Rails.

Three of our colleagues participated as mentors in the workshop - Nemanja is an experienced mentor who has already taught at two workshops in Belgrade and Novi Sad, and Milana and Jelena were participants in previous workshops, and now they are Rails Girls mentors and our new colleagues at Rendered Text.

Rails Girls Novi Sad Workshop

We are proud to support Rails Girls Novi Sad in their mission to encourage more women to enter the world of technology, and are looking forward to supporting future Rails Girls workshops and seeing more women develop their first Ruby on Rails applications.

Rails Girls Novi Sad Workshop

If you’d like to see more photos from the workshop, you can take a peek at our Rails Girls Facebook album.

To keep up with news about future Rails Girls events, you can follow @RailsGirlsNS and @RenderedText on Twitter, or subscribe to our events RSS feed.

Browse Archive

If you like what you are reading, subscribe to our RSS. You can also follow us on @renderedtext for updates.


Rendered Text is a software company. For questions regarding Semaphore, please visit semaphoreci.com. Otherwise, feel free to get in touch any time by sending us an email.

Rendered Text
Svetozara Miletica 10
21000 Novi Sad