Small Improvements conducts Hackathons every few months which usually involves two days of hacking on an experimental project. Hacking doesn’t imply that it’s a “developers-only” affair either; other departments at Small Improvements like Customer Success and Marketing also get a chance to get experimental too. Hackathons usually give us a chance to really get creative about novel concepts and ideas for the product and company. It is about coming together as people to grow as a team – something that we at Small Improvements value as an important part of internal culture.
From Hackathon to Ship It Week
Being creative with innovative ideas is great, but how would we actually Ship It? We introduced the Ship It Week as new concept to extend our occasional Hackathons from 2 days of hacking to 5 days more serious hacking! In the first two days we had our regular experimental Hackathon, but afterwards had to decide which projects were feasible to “ship” until the end of the week. In the context of a feature in the Small Improvements feature, ship would mean: going live for customers!
I like to think of myself as a coder of convenience. I often tell myself this work is just a means to an end – I am only coding till I can afford to spend the rest of my days lying in a hammock, drinking out of a coconut. But that’s not really true. If it were, I would be pursuing elite consulting gigs, or jumping from start-up to start-up on the hunt for unicorn stock options. Instead, after 15 years in the industry, I am working at a small, customer-focused company that turns a respectable profit building unpretentious B2B software.
The other day the owner, Per, asked me why I chose to apply for a job at Small Improvements. There were several reasons – not least of which was actually being able to talk about this sort of thing with my CEO – but I think my main motivation was because I wanted to help people again.
After a significant project involving making Small Improvements responsive, we came up with some UI ‘rules’ that we in the design team or (‘UI Taskforce’) agreed upon. The longer we worked on this refactoring and ‘cleaning up’ of the app’s style, the more we realised the importance of (finally) having a Style Guide.
At Small Improvements, we often have the front end developers implementing UIs faster than we (the design team) can keep up, so it’s important that they can access this guide.
In the past, we at Small Improvements have sponsored various Angular Conferences and Meetups. So how did our developers end up at React Europe? Let’s backpedal to understand the story behind that.
Small Improvements and React!?
At Small Improvements we decided in early 2016 to shift towards React instead of working to migrate to Angular 2. Internally a lot of our developers had had great experiences with React and its ecosystem in their side projects. We wanted to keep up with evolving technologies rather than being stuck with Angular 1. Even though we already have a large frontend codebase in Angular, in the past 6 months we’ve also been able to implement new components and pages in React. We are confident that it was the right choice for the future. We also experimented with Relay + GraphQL, but it felt too premature to use these technologies at this point.
Development is hard. But communication is even harder – especially across departments and continents. Our SI Process Hacks series will highlight a few simple hacks we wish we had known right from the start!
We deploy many times a day – bugfixes, feature improvements, new options, changes in user flows etc. As a developer, you know when you committed, and you can see your commit go live in real-time:
But as the developer on another subteam, or especially as a non-developer, git-hashes are not really your preferred way of looking at things. It’s tricky to know when what change goes live on what day. Knowledge of changes is crucial, you don’t want to demo the product to a potential client and get caught off guard by that new button on a core screen.
We tried using Slack to keep people in the loop, we considered using a special Trello board, or including even tiny changes into our Release Notes. But everything was just complicated and never quite flexible either.
Our dev team has reached a quite impressive size these days! Unfortunately I just don’t scale to this size. I can’t possibly conduct 1:1s, listen to feedback, give feedback and help people grow in a flat team of 12 people. While I don’t code anymore these days, and have assigned most project management tasks to Feature Coordinators, lots of new responsibilities of a growing company are now on my plate. I just have to lean on others so that every dev team member gets the attention they deserve.
That’s why we introduced the concept of Team Lead two weeks ago.
I recently participated in Softwareskills’ Liar’s Dice competition, and since people have expressed interest to hear about how I managed to win, I decided to summarize the process and results.
As I prize I got 500SEK at Teknikmagasinet (Swedish store), a USB memory and this nice piece of paper 🙂
Liar’s Dice and the Competition
Liar’s dice is a game where each player starts with six dice. One player starts by announcing a bet. A bet consists of a number and a face, for instance four fives. The next player can then either challenge the bet or raise the bet. You can read about the details at https://en.wikipedia.org/wiki/Liar%27s_dice. Softwareskills’ made a competition about writing the best AI. I noticed that it is not clear to everyone what an AI is in this context, and how to get started writing one. So let’s discuss that briefly.
Simplified View of an AI
An AI (actually Intelligent Agent [IA], but I’ll continue to call it AI) in its simplest form can be seen as a program that given a state responds with a valid action. A good AI must, in addition to producing valid actions, produce the best valid action given a state. What best means is the real challenge which typically requires mathematical analysis and creativity to figure out. In some cases you might calculate what the best action is, but it might not be easy given that you don’t know how your opponents behave. In the case of Liar’s dice you don’t know your opponents’ dice and they might behave differently depending on what dice they have. Let’s have a look at how I approached this challenge.
The Development of My AI
First off, I’m not developing AIs on a day to day basis. I had some experience from before, but not much. I started by drawing the “Simplified View of an AI” on a piece of paper, this made it easier for me to break the problem down into the main components:
Generate all valid actions given a state
Put a score on all valid actions
Pick the valid action with the highest score
Let’s look at each problem separately, starting with how to generate valid actions.
To generate actions you need to create a function that given the current state and the rules of the game can produce all valid actions. This is pretty boring work that I wanted to get through as quickly as possible to move on to what could actually get me a good place on the top list. Softwareskills’ provided a model with a track that has 27 positions. A die on a position corresponds to a specific bet with the face of the die (some positions require the bet to be a star, where star = six). Now, either the bet can be challenged or raised. A raise can either be of a “higher” face on the same position or any face at a later position. So to generate valid actions I separated the problem into the following sub problems:
Find the position of the current bet on the “track”
Given the position and face:
Get all bets with a higher face on the same position
For all subsequent positions, generate bets with all possible faces
This allowed me to quite quickly generate all possible actions. Now, to value all the possible bets, provide them with a score, I needed to revisit some basic statistics.
When I first saw the competition I thought it was just a matter of calculating the optimal action using statistics. I asked myself whether I was more likely to win if I challenged the previous bet or if I raised the bet, and if I raise, how much should I raise the bet. Based on these questions I created new questions which I could directly “solve”, namely:
Given my known dice, and the number of unknown dice in game, what is the probability of the previous bet to be true?
Given my known dice, and the number of unknown dice in game, what is the probability of my raised bet to be true?
Simple was my first thought before I actually got started. I was easy to come up with a solution, and it could even work decently. The only problem was that it was wrong. And to reach the top of the toplist the number of afforded mistakes is pretty low. It was not easy to figure out whether the statistics was properly calculated or not. When I was stuck at a score far from the best, I challenged my code by doing static analysis, which in this case meant verifying the mathematics. I realized, by reasoning, that it was wrong. This led me to reach for my old book from school (Introduction to Probability and Statistics: Principles and Applications for Engineering and the Computing Sciences by J. Susan Milton, Jesse Arnold) as well as to look at a couple of lectures at Khan Academy (https://www.khanacademy.org/math/probability). After a couple of hours studying combinations, permutations and the good old “number of favorable events / number of total outcomes” as well as experimenting with simple examples, I managed to get it right. I jumped to the top but not to the first place. Something was still missing.
I wanted to go for a clean solution based on simple mathematics. But analysing the games made it clear that my AI was often put into bad situations, where no action was particularly good, and that it challenged the bet too often. It was not really playing the game, it was still just following the rules but a bit more intelligently. I started to add a lot of knobs now. For instance weighted averages and factors and terms of various kinds. I created an massive amount of mathematical functions whose graphs looked approximately as I wanted. Many linear (y=kx+m) as well as some polynomials of a higher degree. I experimented by taking into account everything I could think of, such as if I have a larger or smaller part of the game than the previous player or next player, if the game just started, the previous bets of other players and many other things. The factors could influence the score of an action. But I didn’t know how much they should do so. So I created knobs, factors that I could adjust to make the impact higher or lower. Then I tried to find the optimal values for the knobs.
I basically created a big optimization problem which I only could try to solve empirically. I did this in iterations. I created a lot of knobs, and realized that I couldn’t grasp what I actually did. Then I removed all of them and tried to create a new set of knobs. Repeated this a couple of times until I finally overtook the throne, meaning I finally conquered first place on the toplist. Even if I wouldn’t have won, I feel that this kind of competition is very valuable. To share why I think so I will briefly mention some things I learned from participating.
When I started the competition I didn’t even know there was a prize. This was just a nice bonus. I entered the competition for the challenge. I was sure that I would learn something. Afterwards I think my biggest gain is refreshing statistics skills. It’s quite fascinating how easy it was to do so despite not having worked with it for a long time. I also learnt that it is important to carefully measure the results when you have knobs, or parameters, that you can adjust. With the risk of using too big words, I would say that it taught me to be a slightly better scientist by clearly showing the necessity of careful measurements when empirically looking for optimal parameters. From a software engineering point of view it made it even clearer that correctness of the core is very important, and just because something seems correct it might still have bugs in it. Proper testing of the basic units (function in this case) is very valuable. And just because your solution is based on mathematics it doesn’t mean it will be right or wrong. It might still be slightly wrong.
Furthermore, I learnt the value of keeping the code in a at least decent state at all time, to not hinder oneself from experimenting due to the effort being too high. And finally, I learnt the value of to kill your darlings over and over. Even if you are at second place, when you’re stuck, start over and just keep the generic core which lets you build working solutions quickly. Don’t stick to your good solution just because you feel that you invested so much time in it. Challenge it! You managed to come up with it once, and probably learnt from it, so the next time you might come up with something even better.