- More modern design with less work.
- Integrated community interaction (follows, claps, and comments).
- I can stop maintaining a custom markdown parser used to make posts for this blog.
Saturday, May 9, 2020
Deprecated
Friday, March 13, 2020
Slack Etiquette
Slack is a weapon of mass distraction. Here I am talking about the communication platform, not the slang. Slack allows us to very easily disturb a lot of people and waste a lot of people's time, especially with the cost of context switches. To minimize this waste we have compiled a few concrete guidelines for how to behave on Slack.
- Follow the flowchart above.
- When posting a link always include at least one sentence about what the link contains, and why you think it is relevant to the people in the channel. Links to messages or channels are exempt from this rule.
- If there are more than 3 people in a channel always reply in threads.
- Have specific channels, luckily following the flow chart automatically fixes this.
- Use reactions. Reactions are a super convenient way to do voting. But also instead of reply messages:
- agree = +1,
- thank you = pray,
- appreciate it = heart,
- interesting = eyes,
- get well soon = apple,
- should have been a thread = thread,
- yes/no = heavy_check_mark/x, and
- you can easily add custom ones, ie. we have icons for every customer
- You should spend at least one second per person who is in the channel you are posting in on the message.
- Messages in public channels should be readable by anyone, eg. by being in English.
- Put automation in separate channels.
- Don't use @here, @channel, or @everyone unless your post is both urgent and important.
- If you outgrow a channel leave it.
- State explicitly in the description of a channel "how" mandatory it is, eg. "read at least once a week", or "optional"
- Use links to messages when referring to them, and never re-post something, just post a link.
- Have a "411 channel" where people can ask "which channel is relevant for ..." if you don't know where to post something, or whether you should join more channels.
As an example, my organization has possibly hundreds of channels, we have 4 channels just for the office I am in:
-
#location-must-readfor important messages for everyone, "read at least once a week". -
#locationfor stuff that is relevant if you are in the office, like arranging lunch. -
#location-radiofor random stuff like sharing a link you mentioned to someone from the office. -
#location-robotfor scheduled posts, so they are easy to find and don't clutter in the 'human channels'.
When you are new to something: explore.
- when your organization is new to Slack, make a bunch of channels, and see what sticks, delete what doesn't.
- when you are new to Slack, join channels quickly until you find the ones that fit you, leave the ones that don't.
The only thing to do now is: Start Slacking on!
Friday, March 6, 2020
R(eg?)*x Challenge
Regular Expressions
Regular expressions are a very powerful tool. However, they have their downsides, mainly in maintainability. Regexes are often really hard to read or modify, as they are often just a long string of characters with special meaning.
For that reason, we should use them with care in our applications. But that is not the only place where they have their use. Modern editors come with support for searching and replacing using regexes. This is a perfect use case, because, as a one-time action we need not maintain them. We can also see the exact context that it will be applied to, so there are no bug issues.
The Challenge
Keeping our regex toolbox sharp is important, and I recently got a fun regex challenge to practice. In Visual Studio Code paste the following and create a regular expression that matches each of the good lines (it should show 6 matches), and none of the bad lines:
GOOD: "xxx";yyyy;"zzz" "xxx;xxx";yyyy xxxx;yyyy;zzzz "xxx";"yyyy";"zzzz" xxxx;;yyyy;zzz xxx;"yy""yy";"zzzzz" BAD: xxx";"yyyy xx"xx";yyyy xxxx;"yy"; xx";yyyyy";zzzz" xxxxx;";yyyyy "xxxx";";"yyyy"
Cheat Sheet
To save you some time here is a cheat sheet of VSCode regex:
| Regex | Meaning |
|---|---|
. |
Any character |
[a-zA-Z_] |
A character in the range a-z or A-Z or _ |
[^a-zA-Z_] |
A character _not_ in the range a-z nor A-Z nor _ |
X* |
Zero or more X (longest match) |
X*? |
Zero or more X (shortest match) |
X+ |
One or more X (longest match) |
X+? |
One or more X (shortest match) |
X|Y |
Either X or Y |
^ |
Start of line |
\r?$ |
End of line |
Monday, February 17, 2020
Stop Starting and Start Finishing
I am terrible at coming up with new ideas and projects, by which I mean I do it way too much. I get distracted easily whenever I get a new idea for a project. By now this means I have accumulated an immense graveyard of half-finished projects.
As I have always felt the 'learning' was the goal half-finished projects in itself is not a problem. I have always said if you know how to do something there is no point in actually doing it.
Recently though I have decided that I would like to start finishing things. Hopefully reaping more benefits from them. I hope that by combining ideas from other different areas I can create something to help me gain more focus.
Limit WIP, from Kanban
Wanting to limit what I am working on I immediately thought of Kanban. In Kanban, we have a Work-In-Progress (WIP) limit meaning that each workstation can only work on a set number of items.
I figured that this would be pretty easy to adapt to my needs. A Kanban board traditionally has a column for each workstation including one for incoming work (To do), and outgoing work (Done). In my case I have only one workstation – myself – so I need only one column for that. I also don't need either incoming nor outgoing, so really what I want is just one column with a fixed number of places. I settled on 3 places.
I also need a very clear way to free up these spots again, although it shouldn't be too easy, as that would be the same as where I started. I decided to add a specific goal which when reached would allow me to free that place. In some cases, I might set my goal too high or lose interest. To make sure the WIP limit doesn't get deadlocked like that I also add a deadline to each goal. So if the goal is reached or the deadline is passed I am allowed to free up the spot.
Small Batches, from Lean
Having only three activities I can spend leisure time on encourages setting the goals low enough that you can still swap them out quickly. This is very similar to the 'small batches' preached in lean manufacturing. I very much like this property of the system, as smaller goals are more likely to yield successes.
While small goals is a nice side effect, it does get dangouresly close to just setting trivial goals, and then swapping them as quickly as I did without the system effectively not gaining any focus.
Themes, from CPG Grey
The final improvement I made to the system is to change the "activities" to "themes" in CPG Grey style. I am allowed to change these themes only every three months, so they should be broad.
The final system then has three themes changing at most every three months, each with one goal that I am currently allowed to work on. Each goal has a deadline and I am only allowed to change the goal when it is reached, or the deadline expires.
My Hobby Board
My current hobby board looks like this:
| Theme | Goal | Deadline |
|---|---|---|
| Italian Cooking | Ravioli | April 25th |
| Writing | Write a book | January 1st |
| Medicine | Anatomy jigsaw puzzle | March 1st |
Wednesday, October 23, 2019
A New Take On FizzBuzz
I have been hosting a lot of technical interview by now, to determine whether applicants meet our programming and data understanding requirements. A lot of thoughts have gone into the structure and design of the questions to ensure that they tested all relevant aspects and where not 'gameable'.
Before I did my first I went online to find out what other people had done, and immediately came across "FizzBuzz". Then I realized that if I used something I had found online, my applicants might very well have studied for a technical interview and came across the same thing. Analyzing FizzBuzz I determined that it requires an applicant be familiar with at least loops and conditionals.
As it turns out we can use the same exercise to reach higher levels of programming skill. Here are a few variations I came up with to determine an applicants skill level. They are intended to be solved in C# or Java, but any Object Oriented language should present the same solution.
Level 0: Standard
Write a program that takes as input a number, N, and outputs all numbers from 0 to N. But if a number is divisible by 3 it instead outputs "Fizz", if it is divisible by 5 it instead outputs "Buzz". It it is divisible by both it outputs "FizzBuzz".
Level 1: Without iteration
Write the same program but without using while, for, streams, nor foreach.
Level 2: Without conditional
Write the same program but without using if, the ternary operator, nor switch.
Level 3: Without either (almost)
Write the same program but using only one if, no else, ternary operator, switch, while, for, streams, nor foreach.
Level 4: Without either
Write the same program but without using if, ternary operator, switch, while, for, streams, nor foreach.
Conclusion
Although the fact that level 4 is possible is a fun challenge, and shows creativity in an applicant. However, level 3 is much prettier, so I would seldom be disappointed if they stop here, unless creativity is very highly valued in the position they're applying for.
Wednesday, October 16, 2019
A Story About Software Development
Up front I admit that this post is less about facts and more about a narrative. If I may paraphrase George Box: All stories are wrong, but some are useful.
The story starts — like many conversations I've had — with someone saying "I have an idea for an app".
Chapter 1
As it turned out the idea was good, and immediately I opened my laptop and started designing the prototype. In the beginning I was very productive. The code moved forward at close to typing speed (the theoretical limit until a more direct input method is perfected). I could do this because I knew the entire code base, and as the sole developer I could also make all the decisions.
As with virtually any software project it quickly became apparent how huge an undertaking it was. As the months went on parts of the code started escaping my memory, I learned things, changed my style and preferences slightly. Gradually it was like there were two programmers on the project; me and me from the past. In fact there were many "me of the pasts". Thus being the "sole developer" was an illusion. To completely kill the "sole developer"-idea I eventually partnered with two other developers, to speed up progress.
Working with other people turned out not only to be a benefit, but also a cost. Suddenly there were parts of the code I did not know, and had to spend time familiarizing myself with. It was not clear anymore how to make decisions. Having the other developers come to me with every question would not leave me any time for coding. Having them make their own decisions meant that different parts of the software would not fit together, resulting in a weird eclectic app at best, errors and bugs at worst.
Chapter 2
As a programmer my instinct is to try to solve every problem with more structure — another layer of indirection as the aphorism of David Wheeler goes. I decided that we should start by spending some time figuring out what product we wanted to make. We would talk with our future users; Analyzing the potential market. Then we would make as many decisions as we could. Have workshops with paper mockups, and make sure we had the perfect design. Once we had the design for the app we could start building it, safe in the knowledge that all the decisions had been made. Once implemented it would be a simple matter of testing it. And finally releasing it to the users with some teaching material and more workshops. Everything would be heading ever faster towards the end. Just like a waterfall.
Confident that this was the right way to develop software, we hired a user experience guy, and a documentation & workshop guy. Our plan was idiot proof. Or so we thought. Analyzing, and designing went by with no problems, and we had what we felt like was the perfect design. During implementation however was when problems started occurring.
Just like data structures are frozen algorithms, code is frozen understanding, a notion expressed by Dan North. This means that whenever two people understand something differently their code often end up conflicting. As the code base grew larger we had to spend more and more time making sure that we had not broken anything. There are two ways to do this: more communication and investigation before implementing something new, or more testing afterwards. The code we had just added could have broken something somewhere else, so we had to test everything. None of us knew the entire system, so it would be very difficult to test part of the system that we had not contributed to, let alone fixing bugs if we found them. We were grinding to a halt.
Chapter 3
We had to spend ever increasing time bug fixing, or have to spend a considerable effort communicating with everyone to make sure we had the same understanding. We had two major — and very different — issues: there was no common design for the code base, and we spent a lot of time bug fixing.
We looked again to my favorite thing: Structure. We immediately agreed that the code was too complex to completely plan from the beginning, but we felt like we could at least predict how it would look in two weeks. Thus we instituted bi-weekly planning meetings.
As for the quality, we turned our heads to my second favorite thing: Automation. We would have every programmer write a small piece of code that would test their part of the system. In the end we could just run this code to see if the system worked.
Being better safe than sorry we also wanted to increase collaboration, to make sure everybody had the same understanding, further increasing quality. We decided to have everybody pair with someone else, and they would be both responsible for their code. This meant that misunderstanding got caught immediately, and new the practices were more easily followed as there were two to remember them — and make sure they were not skipped.
Closer collaboration in the form of bi-weekly meetings, and pair programming, together with automated testing was outrageous. This was extreme programming, and it greatly reduced the time we spend communicating and debugging.
Having unclogged the drain, we finally finished the first (of three) part of our app. With champagne and party hats in our bags we proudly went to our users and presented what we had built. We had not needed bring the party hats, as what ensued felt less like a party and more like being lion tamers meeting a lion for the first time.
It turns out that while we had been designing and implementing our system, another app — let's call it AstroSoft — had been released meaning that most of our functionality had become irrelevant. "Isn't this the same as AstroSoft?", was how I learned how much a question could hurt, when we had to reply "Yes, but we currently have fewer features, and is probably harder to use".
Chapter 4
In our ignorance we had trusted in the plan. The design we had made were perfect for the users. Unfortunately, much like I changed during the first months of the project. So had our users changed, and the design no longer fit. Before starting part two, I was determined not to end up with another fiasco. I needed ideas. I needed solutions. Being a bit of a fantasy nerd; I needed the 12 knights of the round table... of software development. I contacted my 11 most talented friends and invited them to a cabin where we would stay until we had come up with a solution. In the end a few of them brought some good friends and we ended up being 17.
We started with a basic question: How could we design something for how the users would be in the future? In reality, how could we even know if our product would be relevant when we were done building it? The only thing we could say with certainty was that it was relevant now, and would probably be relevant at least in a month. We realized that this looked a lot like the situation we had had with regards to designing the code base. In that case we decided to have continuous planning meeting, were we planed a little bit. Maybe we could use the same solution here.
In the end we had a document with four values, and in lieu of 12 knights we had 12 principles. Three of the values were sort of derived from my teams previous experiences. Two from our focus on collaboration:
- Interaction over processes, and
- Customer collaboration over contracts
And one from testing:
- Working software over comprehensive documentation
But we also added one that completely invalidated our previous approach:
- Responding to change over following a plan
Most notably — and as stated in our first principle — we would early and continuously deliver valuable software to our users. The ability to adapt, having the code be able to change suddenly, being agile, was ground breaking. Everybody present had experienced similar hardships that we had, and both the values and principles seems self evident to us. "Surely these will never expire" said one of us "we should we should put it on a website; agilemanifesto.org is available". Having a grammar nerd present the response was swift and merciless "That is a contradiction in terms; A manifesto can by definition not be agile. It is technically a Manifesto for Agile Software Development". "Oops, I already bought the domain name". "Well, done is done, let's just hope people don't end up calling it the Agile Manifesto, because they would sound like illiterates".
Chapter 5
Confident like a flying Icarus, I returned to my team and handed them what I felt was "it"; the right way to develop software. If you know the Greek myth you will know what comes next. My team got all fired up, agreed with everything I presented, but when I was done there was a hand in the air. "That's all well and good, but what does it mean in practice? How are we going to do this?" I had been so focused on the big picture I had not thought about how to concretize it.
When you have to be very concrete, I find it useful to pretend I am making a game. A game has to have very specific and concrete rules. Together with one of my co-partners we sat out to make the agile software development game. Being creative can be difficult but another trick I've picked up is to start by coming up with a silly name, one that has no connotation, so you can think about it freely. In our case we came up with the completely absurd word "Scrum".
Remember our end goal was to "continuously deliver valuable software to our users". First we defined who 'the users' are; again with a new term we called them "product owners". The 'continuous' part we already did for planning the code, so we decided that we would release on the same schedule; every 2 weeks. We called the period in between a "Sprint", although looking back this makes it seen like you have to be sprinting all the time, which is not possible, had I a second chance I would have called it "stage". On the topic of silly words I always thought the word "dodo" was hilarious, so I really wanted to include it in some way. We decided to have it stand for "definition of done, overall", to signify that we had to agree on a universal definition of what "done" meant.
We realized that what we were doing was just renaming things. We needed something that stood out. Something colorful. Then it came to me: post-its. I had heard of something called a Kanban board, that they used at factories. What I remembered was there were colorful post-its and columns. Our "Scrum board" would have three: to do, doing, and done (going well with our 'dodo'). We were on a roll so there was no time to investigate the details of Kanban, although I later learned that the power of the columns was to limit what they call WIP (work in progress). We should have probably included a mechanism for that. Would that this desk were a time desk, that I might correct my past mistakes.
We incorporated our planning meeting this time under the name "Sprint Planning", and also added a few more meetings, one for showing our progress to the product owners ("Sprint Review") and one that should enable us sprint even faster next week ("Sprint Retrospective"). We also baked in some more collaboration (and made sure the name of the game was edged into history) in the form of "Daily Scrum", 15 minutes of chit-chat about what each person did yesterday and what they are going to do today. Finally, any game needs a judge, which we called a "Scrum Master". All other players would just be "the Development Team". In order to avoid later disputes we ended up limiting the number of product owners to one. We also cut the "overall" from dodo because it seemed unnecessary.
Chapter 6
Armed with these new wonderful tools, these artifacts, we started building the planned part two of the system. Starting with the most important few features. We made sure that after each two weeks we showed our selected product owner what we had built. After she had approved the features we would have a few people deploy it to all the users. This small subteam was also responsible for keeping the servers running, and generally that the system was operational.
These were the golden days. Being a developer (or dev for short), was glorious. We got to do all these cool features all the time, and did not have to worry about all the pains of deployed nor maintaining them. It was awesome. Apparently though the operations guys (or ops for short) were not quite as happy. As it turned out nor were the users, because the stability was not always what we had dreamed of. In general actually the ops had trouble keeping up. We had also had some pretty bad scares with security so we brought on a security guy to go in and fix it. That was the straw that made our house of cards spill over.
Because neither maintainability nor security had been built into the software at a fundamental level it just was not possible to after-fit them. Although we managed to deliver part two of the system, it was clear that we could not continue like this. We had to combine the goals, and thus the teams. We needed the devs and the ops to work together, so that we might all thrive, we needed DevOps.
As we had matured and got used to this new way of working, we discovered that we could actually deliver even more often. Soon we started doing it every week, then every day, then multiple times a day. Slowly Scrum did not fit anymore. Luckily I had had time to sit down and study Kanban, and what it came from, namely 'Lean'. I realized that there was a simpler way to express what we had put in the manifesto all those years ago. I found that I could encompass everything in three practices, I coined them "the three ways":
- Flow — make sure things are flowing from left to right smoothly and continuously
- Feedback — get early and continuous feedback
- Learning — we need to continuously grow; be better and faster
As was clear, the silver bullet had been the word "continuous" all the time (pun intended). As Anthony Panozzo said "If something is hard, do it more often", the limit of "more and more often" being "continuously".
Chapter 7
Having formed a DevOps team, and following the three ways we very pretty well set. We delivered quickly, reliably, and the quality was better than ever. Very content I remember thinking to myself one morning "This is it. This is the right way to make software". And so i thought for a while. Until I noticed a small wrinkle in the scheme. As it turns out "learning continuously" is not as easy as it sounds. Being stumped again I wrote a letter (yes, he lives in an area with no cell phone service, or even phones, or electricity) to one of my heroes; Isaac.
He replied to me, that when I observe something I should form a hypothesis of what the underlying truth was. I should then proceed to device an experiment that would determine whether my hypothesis was false or not. Perform the experiment and based on the observations I made during it repeat the process.
This was a very elegant way to put it. I did fear that it would still be a bit too vague for some of my colleagues. I decided to reuse an old idea and make another board, this time the columns would be the stages of an experiment. I also fleshed it out a bit to add a bit more of my favorite thing:
- Problems and observations — here we put a post-it every time something makes us roll our eyes
- Options — here we brainstorm what could possibly have an effect on a problem
- Possible experiments — when we have at least 3 options we can promote one or more of them, making them a little more concrete
- Committed — when we have a few possible experiments we concretize them even further, and prepare to actually perform them
- Ongoing — what we are experimenting with right now
- Review — evaluate how the experiment went
- Next — where do we go from here.
With this we have been able to perform 5-10 experiments per week, constantly adapting and improving. It seems to me now the of the three ways the third is by far the most powerful. Maybe we would have even come up with the other two if we had had the third first. It is impossible to say for sure. But to underline this observation I wanted to call this method "Flow", like the first of the ways. And notice that when you take the first letter of every column it spells Popcorn, thus "Popcorn flow". We can make a strong case that evolution is the greatest force of nature, who would have thought that the same would apply to software? But I do feel confident when I say: This is it. This is the right way to make software.
Epilogue
I have tried to include a lot of facts in the story, such as quotes, explanation of fundamental ideas, and the whole chronology of the methods. However most of it is complete fiction. Most notably: no matter how much I wish I could have been one of the signatories of the manifesto for agile software development as the story suggests, this is simply not the case. Nor did I contribute in any way to Scrum, or any of the other methods discussed above.
What strikes me about the story is that most of the chapters (if not all) are "something that works somewhere else" applied to software development.
Waterfall was came from engineering buildings, Scrum came from Kanban which like DevOps came from Lean which itself came from the Toyota Production System, and Popcorn flow is the scientific method — with extra steps.
Why has it taken us so long to (re)discover things that were already invented? Did we really have to suffer through all the hardship? My guess is that there is a tendency for arrogance in our field. We like to develop our own libraries when we can, because ours will be better. We don't generally like to try new things, because we can make it work without. Even when we hear an interesting idea it is often met with "it doesn't apply to our type of organization" or "our type of software", or "our customers". Certainly I am guilty of arrogance myself sometimes; No code is as beautiful as my own. And that is also why I made the protagonist in the story very arrogant.
This is where, I wish I was smart enough, to apply this insight, to realize: what is the next old idea that will catapult us forward?
Thursday, January 31, 2019
Debugging Challenge #6
Previously on Dr. Lambda's blog:
I have deviced a series of debugging challenges, some are easy, some are really hard, all come from real live systems. Good luck!
The Challenge
- 1 point if you can spot where the error is.
- +1 point if you can explain why.
- +2 points if you can explain how to fix it.
@Echo off
set direct=1
echo %cmdcmdline% | find /i "%~n0" >nul
if not %errorlevel% == 1 set direct=0
REM powershell.exe -file .\AdHocScripts\FlushBlobCache.ps1
Set e=%errorlevel%
if not %e% == 0 goto error
powershell.exe -Version 2 -file .\AdHocScripts\FarmIISReset.ps1
Set e=%errorlevel%
if not %e% == 0 goto error
goto success
:error
Echo ERROR, errorlevel %e%
if %direct% == 0 pause else exit %e%
goto end
:success
Echo done
if %direct% == 0 pause
:end
Thursday, January 24, 2019
Debugging Challenge #5
Previously on Dr. Lambda's blog:
I have deviced a series of debugging challenges, some are easy, some are really hard, all come from real live systems. Good luck!
The Challenge
- 1 point if you can spot where the error is.
- +1 point if you can explain why.
- +2 points if you can explain how to fix it.
// @param date string in the format YYYY-MM-DD
function isBeforeToday(date: string) {
return new Date(date).getTime()
< Math.floor(Date.now() / 86400000) * 86400000;
}
Saturday, January 12, 2019
Debugging Challenge #4
Previously on Dr. Lambda's blog:
I have deviced a series of debugging challenges, some are easy, some are really hard, all come from real live systems. Good luck!
The Challenge
- 1 point if you can spot where the error is.
- +1 point if you can explain why.
- +2 points if you can explain how to fix it.
let csv_split seperator str =
let csvSplit = new Regex(
"((?:\")[^\"]*(?:\"(?=,|$)+)|(?<=,|^)[^,\"]*(?=,|$))",
RegexOptions.Compiled) in
csvSplit.Matches(str)
.OfType<Match>()
.Select(fun m -> m.Value.TrimStart(','))
.ToArray()
Saturday, January 5, 2019
Debugging Challenge #3
Previously on Dr. Lambda's blog:
I have deviced a series of debugging challenges, some are easy, some are really hard, all come from real live systems. Good luck!
The Challenge
- 1 point if you can spot where the error is.
- +1 point if you can explain why.
- +2 points if you can explain how to fix it.
int index = addressLine.Length;
for (int i = 0; i < 10; i++)
{
int position = addressLine.IndexOf(i.ToString());
if (position != -1 && position < index)
index = position;
}
string street = addressLine.Substring(0, index).Trim();
string nr = addressLine
.Substring(index, addressLine.Length - index)
.Trim();
Saturday, December 29, 2018
Debugging Challenge #2
Previously on Dr. Lambda's blog:
I have deviced a series of debugging challenges, some are easy, some are really hard, all come from real live systems. Good luck!
The Challenge
- 1 point if you can spot where the error is.
- +1 point if you can explain why.
- +2 points if you can explain how to fix it.
export class ActivityService {
activities : Activity[] = [];
constructor(){
this.activities = [
{
title: "Arrange meeting",
type: "task"
}
];
setTimeout(function () {
this.activities.push({
title: "Meeting",
type: "appointment"
});
}, 3000);
};
}
Saturday, December 22, 2018
Debugging Challenge #1
Previously on Dr. Lambda's blog:
I have deviced a series of debugging challenges, some are easy, some are really hard, all come from real live systems. Good luck!
The Challenge
- 1 point if you can spot where the error is.
- +1 point if you can explain why.
- +2 points if you can explain how to fix it.
private static long IncrementRowVersion(long rowversion)
{
return rowversion++;
}
Saturday, December 15, 2018
Bug Hunt: Debugging Challenge #0
So, I really like puzzles, and challenges. Just like the Javascript Drinking Game. I also used to post programming challenges online, most were extremely challenging, and very time consuming. Therefore I have long been thinking how to make exercises that are both challenging, relevant, and light.
My idea was some sort of debugging challenge. As a developer I spend most of my time doing this anyway, so I figured my colleagues must too, and it would be great practice. Because we are living in a culture were attention span is getting shorter and shorter I knew that the entire thing has to fit on a page, the description should be bulleted, and the code cannot be more than a handful of lines. If any of these fail most people wont give it a second glance.
I decided on a simple point system, starting with a pretty trivial task – to hook the reader. Just identify what looks wrong. Then draw them in by asking why they said that, and finally the real challenge: How to fix it. The Bug Hunt has a start date and an end date, and everybody collects points all season. At the end the victor can have his pick of which project to work on.
After spending a year keeping an eye out, I finally feel like I have collected enough material for the first "season". All the code comes from actual live systems that I have come in contact with as a consultant. They are obviously annonymized, but the structure of the code, and more importantly the bugs were real. As such, the challenges have very different flavor, some are included because they are very difficult, others are deceptively easy. They span different programming languages and technologies, however, in most cases this context must be inferred.
I highly recommend that you share challenges that you like (I print and hang them on the walls in the office), you might even use them to set up a Bug Hunt at your own office. If so, I would be happy to provide the answers.
Let the 0th Bug Hunt begin, and a good hunt to all participants!
The Challenge
- 1 point if you can spot where the error is.
- +1 point if you can explain why.
- +2 points if you can explain how to fix it.
SELECT a.* FROM Notification a
LEFT OUTER JOIN (
SELECT CONVERT(uniqueidentifier,
MIN(CONVERT(char(36), b.[ID]))) AS ID
, CAST(b.[Message] AS varchar(255)) AS Message_varchar
, b.[To]
FROM Notification b
GROUP BY Message_varchar, b.[To]
) AS KeepRows ON
a.[ID] = KeepRows.[ID]
WHERE a.[Sent] = 0
AND KeepRows.[ID] IS NULL
Saturday, January 13, 2018
Isabella: Testing, and a Build Pipe-line
Previously on Dr. Lambda's blog:
In a previous post I presented my newest pet project: Isabella. Isabella is a voice controlled personal assistant, like Siri, Alexa, and others. We have decided to investigate how difficult it is to make such a program. In the last post we made some small changes with big impact.
Now, the continuation...
Strengthening the foundation
As we are in a phase of improvement, this seems an appropriate time to make the foundation more solid: automated testing. On one hand I think I should have done this from the start, but on the other hand...
I am a big fan of Dan North, in particular his [Spike and Stabilize pattern]. With this we treat all code as if it was a Spike, code fast and unstable. Then deploy the feature to the users and see if it is used. Then after a certain amount of time – like a month – come back and see if it is being used. If it is mostly unused then delete it, otherwise if it is used a lot refactor it and write automated tests for it. This way you invest only very little time in code that ends up being unused.
Isabella started as just an experiment and not code I expected to be long lived, I opted for prioritizing new features over a stable code base. Just like stated in Spike and Stabilize. I recently changed my outlook for Isabella; I now expect that I will use (and work on) her for a long time. Said in another way, I have deployed the code, waited, and I now know that the code is being used. So, it is time to stabilize!
Stabilizing the code was an incredible frustrating process, for primarily two collaborating reasons. The first reason requires a bit of explanation. Many non-technical people think that programmers spent most of their time coding. In practice this is far from the truth. Normally with code we spent most of our time searching for and fixing bugs. This I can easily handle. As a child I loved mazes, now I enjoy being lost in a very complex system, fighting to find my way out. I savour the victory when I finally crack it.
During this process I have spent almost all my time searching the internet for answers; which libraries, syntax meaning, and just hard problems. Very boring, and time consuming. And when I wasn't search the internet I was rewriting the same code again and again. I didn't feel like I was making any progress at all. And then finally, even when something worked there was very little visible effect. So it also didn't feel like a victory.
Here is the documentation of my journey, including the problems and solutions I encountered.
Jasmine
My first instinct was: I want testing, I should start writing tests. I had a bit of experience with jasmine-node, so I installed it, and started writing tests. Problem was, because this was client code the way I used multiple files was with multiple script tags. Thus jasmine-node couldn't find any of the dependencies. Adding import statements in all the files was an obvious solution, but that would give errors on the client side.
I did some research and found something called system.js, which would emulate import statements on the client side. This meant having to refer directly to the .js files instead of the .ts files. In spite of this it seemed like a neat solution.
Codeship, Bitbucket, and Git
My next idea was to setup an automatic test-and-deploy cycle. I wanted Heroku to run the app, [Codeship] to test it, and Bitbucket to host the code. So far I had just used Herokus as my code-host. I was faced with an entirely unfamiliar challenge. How to move from one git repo, to another?
I am no git guru. Unfortunately. I wish I could tell you exactly the steps I took to make this work, but I have no idea. I pull'ed one way, then the other, committed, merged, and suddenly I could push to Bitbucket. Codeship quickly picked up the push, and deployed it to Heroku. I'm skipping here a few small issues with some RSA keys for deploying from Codeship.
Gulp
If we think of problems as lianas, software development is like playing Tarzan; we are constantly swinging from one to the next, in a seemingly endless jungle. Usually when I worked on Isabella I would just start tsc -w in the background, and forget about it. Sometimes I would forget to start the compiler which would be super annoying, because then I would push the build to the cloud to test it, and nothing would happen. This was fairly bad, but having added tests it was much more annoying. First, there were now two things to remember (or forget). Sure it also takes a bit longer to deploy, but having Codeship reject a deploy, because I forgot to test it locally was just a slap in the face.
It was time to setup a build tool. I did some research and narrowed the decision down to Gulp and Grunt. To me they seemed fairly equal, and I don't even remember what the deciding factor ended up being. I went with Gulp.
With a build tool the great advantage is that you can add as many post processing steps as you want. As encouraged by [Typescript documentation] I suddenly wanted browserify, and uglyfy. I also wanted it to be "watching", so I couldn't forget anything.
Uglyfy was no problem, watching was easy, browserify was... difficult. As mentioned earlier my test files used imports. In fact this had been quite tricky to achieve. Now it stood in my way, and I was not about to poke that bear. Therefore I abandoned my dream of browserify.
Do-over: Grunt!
As I remember it: I was brawling with an issue with Gulps jasmine-node not supporting the later versions of ecmascript – in particular Promises (which I use heavily). When suddenly I stumble on some blog describing my dream of a build pipe-line. It had everything, a client part, server part, and common part. The server part was tested using jasmine-node, the client part was tested with jasmine, and phantomjs. The client was browserify-ed and uglyfy-ed. There was watching, and the folder structure was beautiful. It was a [fine template] for a project like this.
The only problem was, it used Grunt. I'm not one to be over-confident in my decisions, so if I learn something new I gladly change. Thus I deleted everything I had made up till this point, and tried swapping in Grunt.
This was not problem free, but it wasn't too bad. I ended up testing both client and server with jasmine-node. Isabella is very light on dom, and very heavy on APIs, this I can test just as easily with jasmine-node.
Conclusion
Although this was a tough stretch I did accomplish a few things. My code is now a bit more secure from my students copying it, due to uglify. It also takes up less space, thus loads faster, also because of uglify. It is browserify-ed, so I can use import as much as I want, and I can never forget to include a file in the HTML. I have testing up and running, so now I can start adding tests whenever I add new features, or fix bugs in current ones. I have a guarded deploy so even if I forget to test locally I am guaranteed that the tests will be run before a deploy.
I don't have any general words of wisdom. I wont say that you should always just Grunt, or anything. Setting up a good pipe-line is hard, but it is also invaluable. It is also a problem that we don't tackle often. I am familiar with the DevOps saying: If it hurts, do it more. Encouraging practicing the skills that we struggle with. If you are afraid of deploying, do it more, so you minimize the risk. If you are afraid of changing some code, delete it and write it again, so you know whats going on. While I agree wholeheartedly with this advice, I don't feel like going through this process again any time soon. If you are about to setup a pipe-line of your own: I wish you the best of luck.
Wednesday, January 10, 2018
Isabella: Change to followup
Previously on Dr. Lambda's blog:
In a previous post I presented my newest pet project: Isabella. Isabella is a voice controlled personal assistant, like Siri, Alexa, and others. We have decided to investigate how difficult it is to make such a program. In the last post we talked about the process and problems of moving her to HTTPS.
Now, the continuation...
Followups
In Isabella there is a concept of a followup, meaning that she is able to reply and wait for a response for certain queries. The most important implication of this is that you do not need to start a followup with "Isabella"; you can just say it. This is what we use for playing games, or taking notes.
Something that often annoyed me was saying "Isabella, turn on the lights", which turns on the lights to their last state. This means that in the morning the lights would be dimmed red, because thats their night setting. Therefore I often had to say "Isabella, bright lights", which switches to their bright setting. I did this sequence, or a similar one until I realized that finding the right lighting is an experimental process. I would often switch scenes a few times maybe try dimming, before I found the exact lighting that I wanted. With this in mind it is annoying to have to say "Isabella" again, and again.
Categories
The solution was so simple, because I already had the followup system in place. I simply added a category to every command, then when I execute a command I just push all commands in the same category to the followup database.
Just like the "refresh" command from an earlier post, this was trivial to implement, yet the user experience was tremendous. Last time I argued that you should prioritize changes which benefit the development, so I wont beat that dead horse anymore.
This time I will point out that only by using the product this became apparent to me. This usage over theory was ironically something I learned while taking a theoretical compilers course at university. The course ended with a competition to see who could make the best peephole optimizer, for a byte-code language. To simplify the challenge we were judged only on the number of instructions. Our optimizers were run on several small, but realistic applications, to see who did the best.
Grip by competitive spirit, we thought up hundreds of byte-code patterns which were stupid and could be optimized. Come the judgement, it was revealed that we were the team with the most patterns, one team had only 7 patterns. "They probably slacked off" we thought, and we were confident that with so many patterns we were sure to win. However, that was not what happened. Everybody clapped and cheered as the team with 7 patterns accepted their victory.
It took months of pondering before I fully realized what had happened. The other team had understood when the teachers said "realistic applications". So they had spent their time, not coming up with stupid patterns like my team, but instead coding realistic applications, and looking at the byte-code. Doing this they had spotted 7 weird, but very common patterns.
Then lesson I learned then has stayed with me since. I knew already to "optimize for the common case", yet I did not know the value of investigating what "the common case" is. My advice this time is: remember to walk in your users shoes once in a while. Sometimes it will reveal a tiny change with a huge effect.
"Isabella"
Getting back to the followups. I noticed that with Amazon Echo I would say "Alexa", and then wait, to make sure she was listening. If you have a long, or complicated command it is tedious to repeat it because it didn't hear its name. Again this was a nice touch, which was simple to add to Isabella. I just added an empty command, which would push all commands to the followup database.
Here is a video:
Saturday, January 6, 2018
Isabella: HTTPS
Previously on Dr. Lambda's blog:
In a previous post I presented my newest pet project: Isabella. Isabella is a voice controlled personal assistant, like Siri, Alexa, and others. We have decided to investigate how difficult it is to make such a program. In the last post we discussed how we added Spotify and OAuth to Isabella.
Now, the continuation...
Deploy Debriefing
Having put Isabella in the cloud there are certain things I have struggled with, and some I still do. On localhost we can easily get access to the microphone and speakers. However, online Chrome won't even ask for permissions if it is not an HTTPS connections. At first this might not seem like such a big deal, Heroku immediately supports HTTPS, so adding the S should just work. Right?
Server-side protocol
Part of OAuth is to have the third party (Spotify) redirect back to use. At first we just used
redirect_uri: 'http://' + req.get('host') + spotify_conf.redirect_uri,
Now we needed to find out if we should add the S. Doing that – server-side – turned out to be a bit annoying. Let me save you the trouble:
let protocol = req.headers["x-forwarded-proto"] || "http";
HTTP requests
We also used several APIs in the client, which only run on HTTP. Let alone the blatant security problems of having all the API keys in the client. This violates the HTTPS agreement, so Chrome kindly blocks them, and warns the user.
Again there is a simple – albeit tedious – solution to both problems: to move all the HTTP calls to the server. That way the client only has HTTPS calls (to the server and Spotify).
app.get('/joke', (req, res) => {
request.get({ url: "https://icanhazdadjoke.com/",
headers: { "Accept": "application/json" }},
(error, response, body) => {
res.send(body);
});
});
We do have to be a bit careful while doing this. Eg. we use an API to lookup our location based on our IP. Obviously if we just move this call to the server, we will get the servers location. As luck would have it, this particular API allowed us to input a specific IP and look it up. Now we only need to find the clients IP, and pass it along. Again this was cumbersome because we are sometimes running localhost, and sometimes not. Anyway the solution is:
app.get('/ip', (req, res) => {
let ip = req.headers['x-forwarded-for'] || req.connection.remoteAddress;
if (ip === "::1") ip = "";
request.get("http://ip-api.com/json/" + ip, (error, response, body) => {
res.send(body);
});
});
Whew... or I mean Hue
Having solved these problems it seemed like we were ready to go full HTTPS. But not without one last problem. Hue. The Phillips Hue API runs locally, and uses only HTTP requests.
I don't want to rant about the general implications or irresponsibility of this.
Because the calls are to a local IP I cannot move the calls to the server. From my search: I cannot change Hue to run HTTPS. So I'm stuck. If anyone has a solution or even suggestions on how to solve this I am all ears.
So, the slightly uncomfortable conclusion is that: with the exception of warnings from each Hue call, we have successfully moved to HTTPS!
Wednesday, January 3, 2018
Isabella: Spotify and OAuth
Previously on Dr. Lambda's blog:
In a previous post I presented my newest pet project: Isabella. Isabella is a voice controlled personal assistant (VCPA), like Siri, Alexa, and others. We have decided to investigate how difficult it is to make such a program. In the last post we finally deployed her to the cloud.
Now, the continuation...
Voice Controlled Personal Assistants
As Isabella has grown, I have started to grow more and more dependant on her, and indeed more attached to her. In the beginning this was just a fun experiment to see how difficult it was to make something like Alexa. At the same time I was strongly considering buying a "real" VCPA like Amazon Echo or Google Home. This doesn't seem reasonable anymore. The other VCPAs do offer a few features that Isabella doesn't have... yet. To balance it out I have decided to add a feature to Isabella that aren't available in the other assistants.
Spotify
I listen to music quite a lot. Wether I'm working, or cooking, Spotify is usually playing in the background. Again I don't want to get into a discussion about which music streaming service is best by any measure, I just happen to use Spotify. Unfortunately playing music from Spotify is not supported by Amazon Echo – in my country, at the time of writing. Of course this is due to politics and not technology. However I still want it.
Research
Spotify has great documentation for their web api. My first idea was just to get some audio stream, pipe it into an audio-tag and boom, music from Spotify. Unfortunately this turned out to be impossible. You can only retrieve a 30 second clip of a song.
This was quite the roadblock, and it stumped me for several days. I looked over the API again and again, and it just seemed to have methods for searching, and "clicking" the different buttons in the interface. In a way the commands in the API could make a remote control. Then it hit me. A remote control was exactly what I was trying to build. I didn't want to build an entire music streaming platform, I just wanted to control one.
This does have the limitation that Spotify needs to be constantly running in the background. But it does also mean that Isabella can control Spotify playing on other devices like phones or tablets.
OAuth
The first step when working with the Spotify API is to implement their OAuth protocol. Luckily Spotify's OAuth is super easy to implement due to their documentation. Most people know OAuth only from the "login in with facebook" (or google), but it can do much more. I imagine that we will use this same protocol for many APIs that we add in the future, like calendars, email, etc. Therefore I briefly explain the basics of OAuth. In my experience OAuth is difficult to grasp at first sight, so you should not expect to gain a deep understanding from this presentation.
Because repetition is good for understanding, I'll explain it using two metaphors I like. Then I'll explain it with the technical terms, because repetition is good for understanding.
Imaging that we are managers in a ware house. We have access to many areas, some of them are restricted, meaning only we have access to them. Now, for some reason we want someone else to solve one of our tasks. But in order to solve this task they need access to some of the restricted areas, that we have access to. This is the fundamental problem that OAuth solves.
The protocol states that:
- you ask the person who should perform the task.
- the person asks the secretary for a key to the restricted area.
- the secretary calls you to ask if this person is allowed into this particular restricted area.
- you confirm.
- she writes an official form and gives to the person.
- the person takes the form to the janitor.
- the janitor makes the necessary key and gives it to the person.
At this point the person can perform the task. We could imagine the same procedure if you are applying for a job, and the company wants to know your grades, which are usually secret. The protocol states that:
- you send a job application.
- the company asks your school (or university) for your grades.
- the school calls you to ask if this company is allowed to see your grades.
- you confirm.
- she writes sends a link to the school.
- the company opens this link in a browser.
- the browser shows the company your grades.
Finally let's take the concrete example of Isabella and Spotify. The protocol states that:
- you want Isabella to control Spotify, so you send a request to Isabella.
- Isabella redirect this request to Spotify, adding some authentication information, so Spotify knows who "Isabella" is.
- Spotify then presents you with a "this application wants access to these areas".
- you click confirm/continue – ie. sends a request to Spotify.
- Spotify redirects this request to Isabella adding a special token.
- Using this token Isabella sends a request to Spotify.
- Spotify returns an access_token.
Basically every call in Spotify's API requires this access_token.
The first step
The first step in the protocol is to show that you want Isabella to take control of Spotify. The standard way is to have a button, and that was my first approach too. This is because the first time you click it, it takes you away from Isabella, and you are confronted with a screen. From a Human-Computer Interaction view point, this view change is feedback, so it is fitting to have a button. However, any subsequent times you click it, Spotify remembers your consent and just sends you straight back to Isabella without you noticing it. This means that in the subsequent cases we have a button without noticeable feedback – not good.
Common computer science knowledge teaches us that we should optimize for the common case. Imagining that you want Isabella to take control of Spotify often... very often. We only log in "for the first time" once. The common case is clearly the subsequent times, where it does not make sense to have a button. Therefore I decided in the end to remove the button, and add a command to "log in to Spotify".
Now this does cause a problem with discovery; the process by which a user learns about features. It is easy to see a button and try to click it. It is harder if there is no visual ques. However this is a general problem for VCPAs, how do you know what you can do with it? With human interaction we assume that either the receiver know how to answer our query, or we can teach them. Is this an approach we can take with VCPAs? Start with a broad basis of tasks, and the have the users teach them what they need? How should they teach it? I will certainly look deeper into this in a later post.
For now, here is a video:
Wednesday, October 11, 2017
Isabella: Deployment and Gender
Previously on Dr. Lambda's blog:
In a previous post I presented my newest pet project: Isabella. Isabella is a voice controlled personal assistant, like Siri, Alexa, and others. We have decided to investigate how difficult it is to make such a program. In the last post we taught Isabella to play games, to improve her mood.
Now, the continuation...
Deployment
I finally got around to deploying Isabella to the cloud, so I could test her on other devices.
I have multiple computers which I use for different work tasks. As Isabella was a project related to my teaching, she was only running on my teaching-computer. This means that every time I got a new idea I had to find my other computer, wait for it to open, find the folder, open the editor, and finally write down the note, or implement the feature.
This of course meant that some times, if it was a small idea, I just wouldn't bother. I have spent a lot of words arguing for eliminating all annoyances and obstacle connected to coding, and yet this is pretty much the biggest impediment I can think of. And it is so easy to get rid of.
First we decide where to deploy her to. I am a big fan of Heroku, so that was the obvious choice for me. Then it is as easy as:
- calling
heroku create [project-name]. - create a
Procfile
web: node index.js
- create a
.gitignore
node_modules
- make a trivial server script
import * as Express from 'express'; let app = Express();
app.set('port', (process.env.PORT || 5000));
app.get('/', function (req, res) {
res.sendFile(__dirname + '/index.html');
});
app.get('/*', function (req, res) {
res.sendFile(__dirname + req.url);
});
app.listen(app.get('port'), function () {
console.log('listening on *:' + app.get('port'));
});
- and finally commit everything with git
git add . git commit -m "Deploy to Heroku" git push
Of course she should have been in version control from the beginning, which would have meant I could easily clone her on my other computer and run her locally there too. It would also enable me to develop on her on the other computer, eliminating the impediment discussed above.
Gender
Once deployed to Heroku I could not wait to test her out on my other computer. I flew over to it, opened the URL, hit enter, and held my breath. She loaded, and finally I heard the words I was waiting for: "I am listening", spoken in a deep male voice. I was stunned, and then I broke out in laughter. Of all the things I expected he switching gender was not one of them.
I had already implemented functionality for changing her name, so the first thing I asked was "Can I call you David". This was very originally to solve the problem of having two devices listening at once, I needed a way to distinguish them. It was impressive to me how much effect the name had, just because I am calling her Isabella, I was completely set on her being a her.
I had previously played around with different voices, to make sure I was using the one I liked best for text-to-speech. But now I actually needed it, so I added a new command for changing her voice.
In order to complete the feature, I added a list of the most common male and female names, so that when you change her name, if you choose a male name, she will also try to find a male voice, and vice versa.
Saturday, October 7, 2017
Isabella: Games
Previously on Dr. Lambda's blog:
In a previous post I presented my newest pet project: Isabella. Isabella is a voice controlled personal assistant, like Siri, Alexa, and others. We have decided to investigate how difficult it is to make such a program. In the last post we explained our reasoning for adding feelings or moods to Isabella.
Now, the continuation...
Games
Now that Isabella have feelings, it makes sense to think about it a bit. What happens if she gets in a bad mood? How can you make her happy again? How do people make each other happy?
Obviously we cant give her a gift, or a hug. Although now that I'm thinking about it, it is not a bad business idea, making an Isabella gift shop, where you can buy virtual gifts for her, to improve her mood. Especially – let's be honest – she is just a fancy, useful tamagotchi.
Another way we humans improve our moods, is by playing games. This we can do with Isabella, and then give a boost to her mood. But which games can she play? The easy answer is: pretty much every game you can play while driving. The first one I thought of was "20 questions", and the easiest I could think of was: guess a number. So let's look at both in turn.
Guess a number
Guess a number is a very simple game, where one player thinks of a number between 0-100, and then the other player has to guess it, using as few guesses as possible.
The cool thing is that this game is exactly what you would expect a computer to like, based on popular preconceptions.
This game was really easy to implement (both ways), using our follow-up system. When she is thinking of the number, you just compare the input with the number and say higher, or lower. When you thinking of the number, she just uses binary search – like any good computer.
Akinator
20 questions is quite a bit more complex. But luckily, like so many times, somebody has already made a brilliant game called Akinator which is exactly what I want. Even more lucky: it has an API. Unfortunately, the API has no documentation. The closest was a couple of projects on github which tried to use it as well.
Unfortunately their code was not quite what I was looking for, so I, instead, made my own, very thin layer on top of the API. Maybe it will be useful for someone else, so here it is:
type StepInformation = {
question: string,
answers: {
answer: string
}[],
step: string,
progression: string,
questionid: string,
infogain: string
}
type AnswerResponse = {
identification: {
channel: number,
session: string,
signature: string
},
step_information: StepInformation
}
type CharacterResponse = {
elements: {
element: {
id: string,
name: string,
id_base: string,
proba: string,
description: string,
valide_contrainte: string,
ranking: string,
minibase_addable: string,
relative_id: string,
pseudo: string,
picture_path: string,
absolute_picture_path: string
}
}[],
NbObjetsPertinents: string
}
class RawApinator {
private session: string;
private signature: string;
private step = 0;
constructor() { }
hello() {
return new Promise<AnswerResponse>((resolve, reject) => {
$.ajax({
url: 'http://api-us3.akinator.com/ws/new_session?partner=1&player=maxipaxi',
dataType: "jsonp",
error: reject,
success: (data: { completion: string, parameters: AnswerResponse }) => {
this.session = data.parameters.identification.session;
this.signature = data.parameters.identification.signature;
this.step = 0;
resolve(data.parameters);
}
});
});
}
sendAnswer(answerId: number) {
return new Promise<StepInformation>((resolve, reject) => {
$.ajax({
url: 'http://api-us3.akinator.com/ws/answer?session=' + this.session
+ '&signature=' + this.signature + '&step=' + this.step
+ '&answer=' + answerId,
dataType: "jsonp",
error: reject,
success: (data: { completion: string, parameters: StepInformation }) => {
this.step++;
resolve(data.parameters);
}
});
});
}
getCharacters() {
return new Promise<CharacterResponse>((resolve, reject) => {
$.ajax({
url: 'http://api-us3.akinator.com/ws/list?session=' + this.session
+ '&signature=' + this.signature + '&step=' + this.step
+ '&size=2&max_pic_width=246&max_pic_height=294&pref_photos=OK-FR&mode_question=0',
dataType: "jsonp",
error: reject,
success: (data: { completion: string, parameters: CharacterResponse }) => {
this.step++;
resolve(data.parameters);
}
});
});
}
}
Here is a short video of us playing a game.
Wednesday, October 4, 2017
Javascript Drinking Game and the Scientific Method
Spoiler warning: this post contains a minor spoiler of the Goal.
Introduction
I am a big fan of Java Puzzlers. I love quizzes and games like that. At university I would regularly host a Java quiz. In the quiz I would present a few lines of Java-code, and then the audience would have to choose from 5 options what the output would be.
Following Atwoods law: "Everything that can be written in Javascript, will eventually be". For this reason I think it is important to learn about some of the pitfalls and corner cases in Javascript. Therefore I have invented a game for learning some of them in Javascript.
The Scientific Method
The game consists of a lot of tiny code snippets, grouped into small sections. Many of these sections are structured to encourage the player to discover an underlying rule. The way you play this game is much like the Scientific Method:
- Make an observation
- Form/refine a hypothesis
- Test the hypothesis
- Repeat
Learning to work like this is very useful, in many different areas. It gives us the ability to uncover the underlying structure of something, without being able to observe it directly. This is also a point in the Goal.
This is exactly the same with these games, you have a language like Javascript, with some fixed rules (the interpreter), of course in this case we could lookup the source code of the interpreter, however we don't want to do that. Even if we did the behavior might stem from a complex interaction in the code. Thus we cannot observe the rules directly.
The way we play the game is:
- You see a code snippet (make an observation)
- Try to guess what it does (form a hypothesis)
- Run the code (test the hypothesis)
If your prediction was inaccurate you refine your hypothesis to include the new data, and at this point move onto the next snippet. As mentioned the snippets are grouped into sections which encourages the formation of good hypotheses.
The Game
The rules are simple:
- Open the developer console (f12).
- Type in the expression
- Try to predict what the result will be
- Evaluate it
- If you were wrong you drink (an appropriate amount)
So without further ado here are the snippets.
> "1" + 1> "1" - 1> "1" + - 1
-
> let x = 3 > "5" + x - x
> "5" - x + x
> parseInt("")> isNaN("")> typeof NaN
> throw "ball"
> []+{}> {}+[]> []+{} === {}+[]
> null > 0> null < 0> null == 0> null >= 0> null <= 0> NaN < NaN> NaN >= NaN
-
> function dis() { return this } > five = dis.call(5)
> five.wtf = "potato"> five.wtf> five * 5> five.wtf> five++> five.wtf> five.wtf = "potato?"> five.wtf> five
> "1000" == "1e3"
> new Date("12/08/2016")> new Date("2016-12-08")
> 0.1 + 0.2
> +[]> {} + []> {}{}{}{}{} + []> [] + []> {}{}{}{}[] + []> [] + {}> {}{}{}{}[] + {}> +{}> {} + {}> {}{}{}{}{} + {}
> []?true:false> {}?true:false> ({}?true:false)
> [1001, 101, 1, 2].sort()> [1, 2, 101, 1001].sort()> [6, -2, 2, -7].sort()
> [1,2,3] == [1,2,3]
-
> let t = [0] > t == t
> t == !t
> Math.max() > Math.min()> Math.max() < Math.min()
> true + true === 2> true - true === 0> true === 1
> [] == false> [] ? true : false
-
> let numbers = [1, 2, 3] > numbers.map(n => { value: n })
-
> Ninja = function (name) { this.name = name; } > Ninja.prototype.jump = function () { console.log(this.name + " jumped"); } > let john = new Ninja("John");
> john.jump()> setTimeout(john.jump, 1000)> setTimeout(() => john.jump(), 1000)> let callback = john.jump> setTimeout(() => callback(), 1000)> callback()
> "" == false> false == "0"> "" == "0"
> [[][[]]+[]][+[]][++[+[]][+[]]]
> ('b' + 'a' + + 'a' + 'a').toLowerCase()
> console.log(b)> console.log(b); var b;
Have fun with Javascript, and always drink responsibly.
