Over the last couple of months my team at Flytrex had occasion to use intervals more than once, and in both cases the team asked me, “What’s the right way to solve this?”. Since this is a common problem, I thought I’d write a short post about it.
Most interval problems seem deceptively simple, while they require more work than expected. Also, programmers aren’t always aware they are working with intervals.
Here are the two problems:
Given the current time and a list of opening hours for a business, determine if the business is open or closed, and if it’s open – say when it will close, and if it’s closed say when it’s open. For Flytrex this is further complicated because we need to find the intersection between a restaurant’s opening hours and the opening hours of the delivery center.
Produce a shift report for a delivery center – show the list of opening shifts (period when the center was active) – and the total flight duration for each shift (by summarizing the flight time for each delivery for each shift.)
First let’s define intervals – an interval is represented by a pair of numbers (a, b) such that it includes all the numbers between a and b. An interval may be closed or open on either side, e.g.x <= b for a closed interval or x < b for an open one. In addition, it’s sometimes useful to allow a to be -inf and b +inf.
Now we can define operations on intervals – union, intersection and inversion, such that the input and output of each operation is a set of intervals.
As an anecdote – one of the questions I ask candidates in Flytrex includes an intersection between intervals – check if two intervals intersect. The naive solution is “let’s map all the cases”, which is hard. The easier solution is to understand the cases when they don’t intersect (b2 < a1 OR b1 < a2), and then NOT the result.
The first trick for solving interval problems is to realize they are interval problems. One of our developers asked some friends about his particular problem – and most suggestions revolved around lookup tables and binary search, which might work but are complex and not a direct solution.
The right way to solve an interval problem is using an interval tree. The second trick is of course not to develop one yourself but instead to find a library. For the developer faced with the interval problem – it transformed a very complex problem to 5 lines of easy code. My hope with this post – the next time you are faced with an interval problem – you’ll know how to solve it the right way – which is also the easy way. Let me know if you do!
It seems common practice to have a staging and production branches for deploying your code. A common pattern is to push (or pull request) to these branches, then merge the changes. Then, some system watching this branch will notice, and deploy to the appropriate environment. (Another way this is done is with tags, but I will not get into it here.)
For us it is slightly different. At least for one of our projects we have multiple deployments, where each can be on a different environment. In addition, it is useful to have multiple staging environments for use by our R&D and product teams, and multiple “pre-production” environments for use by our lab team. So we have staging, staging2, staging3, and lab, lab2, and possibly soon lab3 as well.
One problem with merging to deploy, is that merges create a new commit ID. Rebases are even worse in that regard. When I’m deploying version 1.5 to staging2, I definitely don’t want to change version 1.5, and I want the commit ID to be identical to the one in the version 1.5 branch. When you merge, you accrue merge commits, that are different between various deployments.
So our solution to these problems:
All deployment branches are named deploy/ENV_NAME, e.g. deploy/lab or deploy/staging.
In order to deploy say, release/release1.5 to deploy/staging, we follow the following instructions:
Find out the the commit hash of the last commit of the branch to be deployed. You can do this in the Github UI by switching to this branch, or run something like: git log origin/release/release1.5 --pretty=%H -1
git push -f origin COMMIT_HASH:deploy/staging
This last command is the interesting one. It tells git to have the deploy/staging branch on origin to point to COMMIT_HASH. This is incredibly useful – with this all your deployments of a given version will have exactly the same hash, which will easy version tracking.
The downside is that our git history doesn’t reflect deployment history – but that’s ok – it shouldn’t. It should only reflect only the code history. Deployment history is kept by our deployment software – in our case Jenkins.
I’m interested in learning of more deployment strategies. How do your deployment environment look like? Are you SaaS with a single production? Cloud based with dedicated servers per customers? On premise? What deployment approach works for you?
It always starts with an itch. Today, there aren’t really many good apps to teach kids to read Hebrew. The best one is “Kesem’s Monsters” – and when I tried getting my son to play it, it annoyed me. He liked playing it – but I thought it wasn’t teaching reading all that well.
About a year ago, I was already finished the German skill tree in Duolingo – and I knew what I was looking for. There is an important difference between teaching an adult a second language, and teaching a child to read. While the problems are similar, the adult already knows how to read – it’s a basic skill that is generally transferable between many languages. The child however is normally a native speaker – they already have a good vocabulary – but they need to learn to read, a completely new skill.
So about a year ago, I started working on a reading app for my son. I wrote it in Swift for iOS, which was a fun experience learning a new statically and strongly typed language. The main game for the child was: see a written word – and tap the image it represents. So for the word “Horse” – the child would need to tap the horse icon, as opposed to the house icon.
Here is a demo of the app:
The app is already available on the app store here. I also built a site for it. Following are 5 lessons I learned while working on this app.
1 – Your free time can make an impact
Working on a startup while having kids is draining. Not just your time – but your energy too. A few years back I decided not to feel guilty about not having the energy to work on a side project. It’s ok at the end of the day just to sit on the sofa and watch some Netflix with your significant other.
However, sometimes you do get that itch to build something. It builds up in you. And when that happens you will find some time. An hour when everyone is taking their afternoon nap on a Saturday. Another hour at night when everyone’s already sleeping.
And the reward in satisfaction is there. You can look at your time and say “I built this!”. It doesn’t have to be the next billion or even just million dollar startup. It can be something as small as a blog post. But it will have an impact.
With this app I knew I was really onto something when I got this feedback from a beta tester (translated from Hebrew):
“Thanks to you, a young first-grader who we just recently discovered also has a development disability (he is a foster child) is advancing his reading and enjoys using the app. Thank you!”
With this, I knew that even if I stopped there, and there won’t be a single additional installation of this app, I made some impact. Not every idea or project you and I will build will have this specific impact. But they will do something.
2 – How to motivate children to learn by playing
Humans are amazing reward optimizers, and children are even more so. Here are some learnings on what worked for me.
Provide visual and audio reward and encouragement for every success
Make it clear what’s an error and what isn’t
Don’t leave loopholes for getting “progress” without learning, children love exploiting those!
A bug I described here – my kid would click multiple times on a right answer to earn the points for it multiple times
For an image with alternative words you need to pick from – the child will read only the first syllable to find the correct word
The meta-game is critical!
In my app I added a reward screen where every 5 levels you get another animal added to this reward screen. This encouraged children to continue playing through the levels to get all the rewards.
Be careful with negative feedback
In an early version, my son would visibly lose points on making a mistake. He got really upset and stopped playing, which surprised me, I didn’t expect such an emotional connection to the points he earned.
3 – A single good experience is not good enough
From the people who installed the app, or tested it I got very good feedback. It’s a great feeling! However, many children played through all the levels once in about an hour, and then stopped playing.
Learning from that – the next major version of the app will have a different engagement model – where I would aim to get the child to play a little more each day. The rewards need to be built differently, and long term engagement needs to be the focus.
4 – Spend just a little bit of money and your work will look professional
Initial beta versions of the app had a white background and I recorded the narration myself. These days, getting some quality graphics and good narration does not cost a lot! You can get very good results for not a lot of money on fiverr.com or upwork.com.
The different response I saw from people before and after I put in these changes was surprising. Before – “cute toy”. After – “wow, you built this?”.
5 – It takes a lot of work to make an app a success
This app is not yet a success. I never expected it to be a great financial success. At most I hoped that some children, including my own, will improve their reading.
To make an app a success takes doing a lot of things, which are hard to do in your free time.
Build a good experience
Support both iOS and Android
Build a website
Build a facebook page and maintain it
Get screenshots and demo videos for the app store page
Write some good copy for the app store page
Promote your app with advertisements
Track users using analytics and encourage them to use/share/review your app
Implement some monetization – be it ads, purchase price of the app, in-app purchases, whatever – this takes work
Happily enough – these days it’s easier than ever to do these things cheaply and easily – but even then it’s still a lot of work.
When working on a side project, you should enter it with open eyes and be aware of what is the most you can expect from the level of investment you put in.
Logging is a critical part of every serious project. If logging is not important in your project – you’re probably doing logging wrong. Here are a few lessons I learned over the years running multiple projects.
1 – Reserve ERROR for errors, and everything that is not a bug in your code shouldn’t be an ERROR
Every log line has a log level. The most important distinction in log levels is between ERROR and everything below ERROR. The following logic should guide you – an ERROR log line should indicate a bug in your code. If there’s an event that generates an ERROR log line which is not an indication of a bug in your code – this should not be logged as an error.
Furthermore, you should spend effort making sure that every recognizable error should be logged as such. So, most handlers should be generically wrapped by an appropriate logger, and your 500 logger or equivalent should naturally emit ERROR logs.
2 – User input validation failures should be warnings
As a natural result of our first tip, user validation failures shouldn’t be logged as errors. They are your code doing what it should be doing. However, they still merit more than an INFO log line. So use WARNING here. Other events can also be logged as WARNINGs, events such as resources running low, a fallback being used, etc.
As a natural outcome of the first two tips, we come to tip no. 3:
3 – Alert on errors, or on multiple warnings
So, your log-levels are now correct. The next step is getting notified whenever an error happens – this is an indication you have a bug in your code. But you don’t want the same error happening a lot to flood your inbox (or whatever other reporting mechanism you use.)
You can de-dup your errors yourself, for example, by hashing the call stack. Alternatively, use a service such as sentry.io to do that for you. You can now send notifications such as E-Mails and text messages when new errors appear.
Once you have that, you can also consider getting alerts for warnings that happen too many times. For example – if a particular user input validation fails often then perhaps your UX is broken. If a fallback happens too many times then perhaps your main flow is not robust enough.
4 – Make your logs informative
Be liberal with adding info logs. At the least, all cross-service and requests to your API should be logged. Other major events/decisions should probably also be logged. Personally I’d probably prefer O(1) per call to my API (i.e. don’t INFO log in a loop).
Independently of that, take care to include all the useful information you can in your logs. That includes file, line, perhaps all or part of your stack trace, and so on. The text logged should also be informative – if a particular value is incorrect log it and the desired value (be careful of privacy concerns though!)
5- Aggregate all logs into a single searchable database
Having a single, searchable log interface, instead of separate ones is critical. Being able to understand the complete flow of an issue is in many cases dependent on you seeing all the relevant information together. Having it searchable will greatly speed up your ability to find issues and fix them. Today at Flytrex we are using logz.io, but there are quite a few other effective solutions.
If your project involves two or more people, decide on a logging policy explicitly.
There’s a big difference between logging in libraries, tools that run once, or long-running programs. Each one has different needs.
For cases when your logs are not perfect (and they never are), a tool such as rookout is very useful. It allows you to set a “logging breakpoint” anywhere in your code – without redeploying it. This already saved me hours of debugging.