I’m Tomas Lamr, principal engineer at Pricefx. We had an interesting problem with limited parking spaces and in the end the holdybot.com app was created to solve this.
The problem was twofold:
a) I wanted to learn Clojure and ClojureScript properly on some project :)
b) Our company is currently in a massive growth phase, it doubles more than twice every year. (If you are looking for a job in sw/pricing, we have offices in Chicago, Brisbane, Prague, Ostrava and Hradec Kralove.) However, since the offices in Czech republic are located in city centers, the number of company parking spaces is limited and on some days it may happen that there is not enough parking spaces for everyone who comes by car.
Automate all the things!
When I worked for my previous company, a brilliant programmer Zdeněk Horák (https://twitter.com/zdenekhorak) created a nice tool called picko, where you could log in, click on a day in the calendar (like https://fullcalendar.io/) which meant: I would like to use company parking space on that day. And once a day a script checks the reservations for the following day, looks into the past and assigns the available parking spaces to the people who used the parking least in the last month. What a great idea! That might work for us as well, I thought.
Learn Clojure and ClojureScript finally!
I’m a huge fan of both but have never finished any serious project in either. This was a nice opportunity to finally learn it, so I started.
Where to start? This might be a bit harder than starting with other technologies. Usually there is some framework that handles most of the technical things for you (like Java and Spring) but the Clojure/ClojureScript world is rather based on a small opinionated libraries from which you build your stack. But which of them to choose if you do not know any?
Fortunately Dmitri Sotnikov (https://github.com/yogthos) created a brilliant luminus leiningen template (http://www.luminusweb.net/) that comes with an already made decisions and opinions about libraries :) I have to admit that I haven’t removed any library, just added some others.
I wanted to have an SPA running in the browser with some backend that would be talking and storing data in Postgres. So I generated my app:
$ lein new luminus myapp +postgres +shadow-cljs +reagent +reitit
This gives you preconfigured project structure that you can build on. Open Cursive IDE and just try to do something.
I need to write another blogpost about technical things. But basically using Clojure/ClojureScript enables a repl driven workflow where you have hot-code reloading for both server and client. Together with the libraries chosen in luminus template and with the help of Cursive IDE this was most pleasant developer experience that I have had in the last 20 years I’m in software development. Thank you guys, for making such a dramatically better tools to tackle the real development problems! :)
The old solution of the parking problem:
Most of the solutions to that problem just work on the “first came first won” basis. All such calendars or reservation systems tend to be the same. The same method was used at our company.
There was a shared Office 365 Excel document that had a column for each parking space. So far so good. Anyone within a company could put their name there when they needed to park and that was it.
However, what happened is that some people just put their names in a single column for half a year in advance and block anyone else. When they knew that they might not need parking, they removed their name from the cell for that day. However, people who needed to park just occasionally saw all the spaces constantly occupied and they were unable to put their name anywhere.
So what did they do? They reached to the office manager and told her the full story why they needed a parking space on a particular day and asked her if she could do something about it. The office manager then needed to ask all the people individually if they really needed parking for that day or if they could pass it to somebody else. This, on a daily basis, with a lot of people, discussing why and when and how to make everyone happy. You can probably imagine that this did not scale and in the long run created a lot of grey hair for the office manager.
Furthermore, this was really bad for the whole branch morale, since it created gossips and negative emotions inside the office.
The new solution:
The first try:
I created an application where you can login through the company SSO and click the date, which creates a reservation request for you (and another click allows you to cancel the request).
Which is pretty clear. Once you click there, you are added, the entry is highlighted in yellow and you will be waiting for the app to decide if you are eligible to park or not. (See below how it decides who should park.)
Once you win, your name gets green and the space is assigned to you.
You’ll receive an email and a notification is published in MS Teams or Slack chatroom.
How the app decides who should park? Imagine you have 2 parking spaces (Space666 and Space999) and 3 people that want to use it that day.
The app looks into the past and finds out who used the parking spaces the least in the last 30 days and will allocate the parking space to those people first. So, it is fair to all people and this simple fact resolves all the negative emotions and solves our social problem :)
Here, Tomas and John won the parking space and Karl didn’t. They all received notifications on Monday evening once the assignment was done.
So everything is clear and easy, right?
So, I thought I’m done and there doesn’t need to be anything else added. So, I presented it to our office manager. But we immediately found out that this solves the employees needs but does not help her at all. She got involved into new never ending discussions about parking, since this created quite a tension among the people who used to have the parking granted anytime and now they would have to “compete” with others. Also, since our company is an international company, it often happens that there are visitors or just internal people coming from all over the world that need parking and so there was still quite a lot to manage for the office manager. But now, she didn’t have a tool to do this.
An admin interface!
We needed to make it really easy to use and helpful, so I added an “on behalf” functionality where the admin (i.e. the office manager) can do the reservation request and related steps on behalf of other people. Just imagine, your vice-president heading to the Prague office calling you: Hey, I have just arrived! Where should I leave the car now?
So, you go to the app, switch to his account and do the action on behalf of him!
So we see two things here. First, current day is 26/08/2019, Monday. As you can see, this day in the picture above has a thick border which means that the space winners were already computed and if you try to make a reservation for that day, the system immediately assigns you a space - if there is a free one. So in this case, Mr. Blueberry can park at Space666.
There are also other actions that allow you to recover from corner cases. All these transitions are going to be covered in the documentation but for now, just check out the screenshot below to see how nice and colourful can your workspace be :)
This got us to an interesting point where everyone was happy with what was done. The office manager could let people do the reservation requests, but if needed, the admin can allocate parking spaces in advance. Furthermore, everyone can see (thanks to the extra icon) that the space was assigned by the admin and not by the algorithm, so it is clear how each space got allocated.
In addition, people can cancel the parking request at any time and the system will send an email to all the others who wanted to park on that day (but didn’t win a space) to let them know that there is newly an available space. (In this case, once Dr. Frankenstein gives up on his parking, Jane Banana will receive a notification and she can take the free spot if she wants.)
So we made the system live. Everyone is happy that parking can be managed easily and with clarity. We added quite a lot of small features that make life even easier but then, the bigger challenge came.
As I said our company grows twice every year (even faster now) and the offices are getting crowded. One solution is to move every other year to bigger and bigger offices, but that depends on many other factors. We currently have a very nice new modern offices in city centers and it is not easy to acquire 4 times bigger office in a similar location with similar properties. This is a management and strategic planning issue since it also impacts the overall cost of running the company.
However, on the other hand our company is a “remote first” company. Most people don’t come to the office, they work remotely, but still everyone has “their” desk in one of the offices even though it is really exceptional that they all would be occupied at the same time.
So, we made an extension to the parking system, so it can be used also for desk sharing. In the desk sharing mode, you add all desks to the application settings. When users log in the app, they will see all days marked green (i.e. they already have a pre-assigned desk owner for every day), but each user can opt for giving it up if they are not planning to come to the office. In return, these users get points to improve their score for parking, so there is more motivation to let others use their desk.
Once someone gives up on their desk, other people can compete for it or the desk can be assigned by the admin to anyone.
In the end you can combine spaces without an assigned owner or without it, no matter if it is for parking or desk sharing purposes. This way you can create any combination of spaces that can not be shared, unless their owner decides otherwise, or spaces that are shareable for anyone within your organization.
Not convinced yet? Try it on https://www.holdybot.com