Notes from an Interviewer

These days I find myself interviewing quite a few candidates. Some are good, some aren’t. So it goes. However, there is another group who I feel could be good but just aren’t showing it in the interview. I think I was probably in this group earlier in my career. So, in a bid to help my younger self, and hopefully somebody else, here’s some things to consider.

1. Know the Basics

If you google ‘top 10 X interview questions’ where X is your technology, and a question is on that list you should probably know the answer. It turns out that interviewers aren’t that original so you’ll find the same questions cropping up again and again. There’s definitely a debate to be had about whether interviewers should ask more original questions, but we are where we are. At least score the easy goals.


2. Know why not (just) what

Great you know what idempotency is but why is it a useful property? There’s probably no quicker way to tell the difference between a junior who knows a lot and an experience dev than the ability to articulate why things are the way they are.

For instance, take the SOLID design principles. Uncle Bob has said that it’s about trying to create a Plugin Architecture similar to that found in, for instances, IDEs. SOLID isn’t just a bunch of sentences that you’re supposed to spout out in an interview, it’s a set of guidelines intended to achieve a certain purpose. Of course, whether it actually achieves that purpose is another matter (see 3.).

This is important for a whole host of reasons but primarily, how can you be trusted to make decisions about what technologies/approaches to take if you don’t understand their purpose.


3. Express Opinions

If a company wanted you to be a dumb vessel for received wisdom you probably wouldn’t want to work there. Just like knowing that things are they way they are for a reason, we should also know that those reasons aren’t valid in certain scenarios.

I used to ask ‘tell me one of the SOLID principle you strongly agree or disagree with’ but I had to stop because it ended up with the interviewee listing/describing the SOLID principles rather than critiquing them.

Another question I like to ask is what is a feature you would add/remove from your language of choice. If I disagree with you, that’s not an issue. Just like the songs Lennon and McCartney wrote together were better than the ones they wrote after, contrasting opinions on a team bring new insights and better decisions.


4. Listen to the Interviewer

I’m genuinely surprised at how often interviewee don’t listener to the interviewer. The question about SOLID (see 3.) is an example of not listening to the actual question which is more common than it should be. However, there is another form of not listening that’s more destructive to a candidates chances.

I’m sometimes in a practical part of the interview where a certain part isn’t going well. It’ll be a point to note, but not a deal-breaker. At that point I’m unlikely to gain any more information from that section so I’ll be keen to get to another section where I can get more useful information, and hopefully they can do better. At this point I’ll try to offer the interviewee some hints or maybe even just tell them what to move them along. However, on quite a number of occasions this advice has been flat out ignored and the candidates have kept ploughing on with whatever failing approach they were using. To put it bluntly, the initial inability may not have been a deal-breaker but ignoring advice is.


5. Be Upbeat

As a final point. A bit of enthusiasm can help a lot. You should never worry if you drop a few clangers because it’s rare that successful candidates are perfect. Probably more importantly, if you are borderline, is that you haven’t left the interviewer with the impression that they’ve been pulling teeth.

And last of all, Best of Luck!

What is the point?

When I started out a programmer I had assumed that the job would entail lots of clever algorithms and complex data structures together with the kinds of design principals outlined by the likes of Uncle Bob and Martin Flower.

It pretty quickly became clear that this would not be the case.

The fundamental goal is to deliver features to customers and/or business users in the most economical way possible. I don’t think there’s anything fundamentally wrong here. If you think that clean architecture (or whatever it is you’re into) matters it’s up to you as a programmer to make the business case for it. Capitalism pays our wages so we should observe the rules of capitalism.

However, this is based on the idea that the primary metric is how much an end user values a feature vs what it cost to produce.

Recently, though, I’m less convinced that this is the primary metric in many cases. Just look around you. Companies like Splunk, Datadog, Palantir etc. rarely, if ever, turn a profit. Those that do tend to trade at ridiculous PEs. A large amount of VC money is tied up in companies that don’t have many end users (much less profit). In this situation the primary metric is no longer the amount of money coming in from end users, it’s the amount of money coming in from the markets.

In other words, the goal is no longer to sell software to customers, it’s to sell hopes to investors.

In this model features must matter in the sense that without feature there would be nothing to sell to investors but the direct relationship is no longer there.

Still, as the old saying goes: “during a gold rush, sell shovels”. So, it’s good that we’re selling shovels.

But what do we do once the gold rush ends?

What should I learn?

I think all developers have had the experience of being told about the X developers earning Y a day and thinking to themselves that’s the gig I should be in. The X and Y vary of course. The most recent one I came across was X = Kotlin and Y = £1200 but I’ve heard all sorts. My favourite was the guy who was offered £1500 a day to write JasperReports but apparently he didn’t take the offer.

Now, how much of this is coder campfire talk and how much is real is hard to tell sometimes. However, what certainly is true is some skills pay better than others. The jobs market is a market like any other and there is an ongoing process of price discovery to find out what a skill is worth.

Let us suppose for the purposes of this article that the JasperReports story is true. I think there’s a plausible situation where a company needed some reports very urgently and didn’t have time for one of their own Java developers, if they had any, to get up to speed. It’s definitely possible in such a situation that the daily cost of not having the reports written was considerably greater than £1500 a day so it makes economic sense to pay the premium.

It worth noting, however, that this was highly unlikely to be a long term market. If it was an ongoing requirement you would just train up one of your own devs and use the contractor to fill the gap. Also, there is no way a developer could have predicted that there would be this market. It was just a skill that they happened to have that turned out to be in demand. In other words, they lucked out but only for a period.

The Kotlin example is more interesting. The reason for the high rate is presumably low numbers of Kotlin developers. But of course there are also going to be low numbers of Kotlin jobs relative to, say, Java jobs. So, you’ll be paid more for a job if jobs are available but you run a greater risk of not having jobs available when you need them to be. I guess the best approach here is to maintain a dual skill set. Have a bluechip skill like Java together with your niche skill so you can fall back on your bluechip if there are no jobs for the niche one.

The Lindy Effect

The other thing to be considered with having a buzzy skill set is the Lindy Effect. The Lindy Effect seems to have gone through a few iterations so I’ll clarify what I mean when I’m talking about it. The Physicist J. Richard Gott III noted that when you observe things they will be on average at the middle of their lives. So, you’ll see some things near the start of their lives and some things near the end but on average the time that something has existed before you observed it will be half of it’s lives. A corollary of this is that if you are trying to predict how long something will exist we should predict that it will exist as long as it’s existed so far. So, if it’s been around 10 years we should expect it to last 10 more.

If we apply this idea to coding it seems to suggest that if we’re interested in longevity we might actually be better off focussing on the likes of C/C++ and Java, rather than, say, Go or Kotlin. This is not to say that Go won’t be around in 20 years and Java will, it’s just that it’s more probably that this is the case than the other way around. If you’re thinking this is counter-intuitive consider two things in the JVM world. Firstly, who is talking about Scala and Clojure these days? About 10 years a lot people were predicting these to be the future. Secondly, consider the sheer amount of code written in Java. I think even if we stopped writing Java today it would take at least 20 years to rewrite it. If you don’t believe me ask yourself why these guys exist. Java will need to be maintained for a long time.

How the Rich get Richer

I’m currently in the process of trying to buy a house in London as a first time buyer and it is not a fun activity. One of the key anxieties is that what you may be able to afford today you may not be able to afford in a few months due to rising prices. It struck me that those who had been able to buy a couple of years ago would have benefited significantly from this rise in prices. Of course the key blocker to many people being able to buy earlier than they do is access to a deposit. Therefore, those with access to a deposit earlier (e.g. those with some sort of family assistance) would be much better off. It’s what economists refer to as the ‘Time Value of Money’.

What I wanted to do was see if I could model how this advantage would play out over time. Here’s the model I went with. Imagine a house costs £300,000 with house prices rising 5% per annum and an interest rate of 3%. We have three buyers, all with the same income which allows them £2700 a month to spend on rent/mortgage and savings. Say rent is £1200 a month. This means that each can save half of the 10% deposit required for a house a year. So they are all in the same situation other than one key difference. Buyer1 starts out with no money, Buyer2 has £15,000 (i.e. half a deposit) and Buyer3 has £30,000 (i.e. the full deposit).

The amount of equity that a buyer will accumulate each month is going to be the amount they save plus, if they have bought, the amount of house appreciation and the amount of capital they pay off on their mortgage.

Here’s what happens over a three year period.

Month House Price Buyer1 Equity Buyer2 Equity Buyer3 Equity
Buyer3 buys with mortgage repayment 1280
1 300000 1500 16500 32025
2 301222 3000 18000 35273
3 302449 4500 19500 38528
4 303681 6000 21000 41790
5 304918 7500 22500 45059
6 306161 9000 24000 48333
7 307408 10500 25500 51615
8 308660 12000 27000 54903
9 309918 13500 28500 58198
10 311181 15000 30000 61499
11 312448 16500 31500 64807
Buyer2 buys with mortgage repayment 1338
12 313721 18000 33494 68122
13 315000 19500 36767 71444
14 316283 21000 40048 74772
15 317571 22500 43336 78107
16 318865 24000 46630 81449
17 320164 25500 49931 84798
18 321469 27000 53239 88153
19 322778 28500 56554 91516
20 324093 30000 59876 94885
21 325414 31500 63205 98262
22 326740 33000 66541 101645
Buyer1 buys with mortgage repayment 1400
23 328071 34961 69884 105036
24 329407 38261 73234 108433
25 330750 41569 76591 111837
26 332097 44883 79955 115249
27 333450 48204 83326 118667
28 334809 51533 86705 122093
29 336173 54869 90090 125526
30 337542 58212 93483 128966
31 338917 61563 96883 132413
32 340298 64920 100291 135868
33 341685 68285 103705 139330
34 343077 71658 107127 142799
35 344474 75037 110557 146275
36 345878 78424 113993 149759

Whilst I was aware that Buyer3 would be better off, it’s the amount that they are better off by that’s striking. They started with £30,000 more but by the end of three years they are over £70,000 better off. Note also that even though all three have bought a house Buyer3 still does better on a monthly basis due to the fact their mortgage payments are £1280 and not £1400 as they are for Buyer1.

The above analysis is performed with house price rises of 5% which is perhaps a little conservative for the market that can be seen in London. If I perform the above analysis with house price rises of 8% I get the following situation after three years.

Month House Price Buyer1 Equity Buyer2 Equity Buyer3 Equity
Buyer3 buys with mortgage repayment 1280 in month 1
Buyer2 buys with mortgage repayment 1382 in month 13
Buyer1 buys with mortgage repayment 1493 in month 25
36 375497 84647 132261 179378

In other words they are nearly £100,000 better off at the end of the three years.

Now, of course, house prices can go down as well as up. Prices dipped in London back in 2008 and they crashed back in 1989. However, even with this in mind the only situation where Buyer3 is worse off is house prices going down between Buyer3 buying and Buyer1 buying. If they fall after Buyer1 buys, Buyer1 still has the greatest amount of debt and thus the highest chance of being in negative equity.

Supposing, that house prices do fall immediately after Buyer3 buys and stagnate thereafter. We can see below the effect that this will have on the situation.

Percentage Decrease House Price Buyer1 Equity Buyer2 Equity Buyer3 Equity
1% 297000 62689 83322 100881
2% 294000 62796 83492 97881
3% 291000 62902 83663 94881
4% 288000 63008 83833 91881
5% 285000 63696 84601 88881
10% 270000 64876 86116 73881
15% 255000 66121 87695 58881

It takes a drop of over 3% to eat into Buyer3s’ advantage. Even a 10% drop has only cost them about a third of their original advantage. By 15% Buyer3 has lost all of their advantage and is worse off than Buyer1. It’s also worth noting that Buyer1 will have lower mortgage payments than Buyer3 so will be gaining month on month (and thus reversing the situation outlined above).

Overall though. The ability to buy early tends to confer massive advantages.

Any thoughts or suggestions are more than welcome.

Robotframework: Python vs Java

Robotframework: Python vs Java

All the tests I’ve described can be run using the scripts from here https://github.com/devwithahammer/robotframework-comparison

The company I work for uses Robot framework to run its’ acceptance tests and I think it’s fair to say that it can be a little slow, especially when generating logs files. Fair enough, it’s an open source library, and it’s a bit churlish to moan about the performance of code that people have contributed to the community freely.

However, it did strike me as odd that log generation was taking so long and not only that it occasionally crashed with an out of memory error when I was running it through RIDE or on a low memory VM. After a bit of investigation I found out that the issue wasn’t, in fact, the robotframework but the fact that I was running it using through Java.

The first thing I did was just run some tests that only printed a log line. I ran 3000 of them.

Language Time Taken (in minutes and seconds)
Java 2m 08s
Jython 2m 18s
Python 44s

For the purposes of full disclosure I’m using a really old version of Ubuntu (10.04), Java 6 and Python 2.6 but what I’m more interested in here is the relative difference between them which is pretty considerable.

Even worse was the memory profile :

Java:
javatests-crop
Jython:
jythontests-crop
Python:
pythontests-crop

So, just use Python right? Well unfortunately not. I’m a Java developer and for various reason all our robot keywords are written in Java. So what I wanted to see then was if the extra time that using Java added was constant but it turns out it’s not.

What I did was write simple keywords in Python and Java that added two numbers together and returned the result. So basically,


public int returnSum(int inputOne, int inputTwo) {
	return inputOne + inputTwo;
}

for Java and


def returnSum(self, inputOne, inputTwo) :
	return int(inputOne) + int(inputTwo)

for Python. I then wrote a script that created 3000 duplicates and 3000 tests that called these keywords.

Here were the results.

Language Time Taken (in minutes and seconds)
Java 2m 38s
Jython 2m 50s
Python 55s

Notice that we’re adding on about 10secs with the Python but roughly half a minute with the Java and Jython.

I wanted to see if the trend continued so I upped the count to 10000 and it looks we’re getting a similar ratio

Language Time Taken (in minutes and seconds)
Java 12m
Jython 11m
Python 4m 28s

Also, we see the same disparity in memory usage.

Java :
java10000-crop

Python :
python10000-crop

and the above is only the memory usage when actually running the keywords. When generating the logs files the Java memory usage jumps up considerably.
java10000logs-crop

Overall it takes nearly three times as long to run the tests through Java as through Python with about 2.5-5 times the memory footprint.

So, the question I had then is, would it be better to run the tests with Python but call through to my Java keywords using a library like py4j? It looks like the answer might be yes.

What I did was take the 10000 test experiment from above and for each Java keyword create a Python keyword that called through to it as follows.

from py4j.java_gateway import JavaGateway

class TestKeyword0 :
	def returnSum(self, inputOne, inputTwo) :
		gateway = JavaGateway()
		return gateway.jvm.TestKeyword0().returnSum(int(inputOne), int(inputTwo))

(see py4j for details on how to set up py4j).

And there’s very little difference in the time between straight Python and calling Java from Python.

Language Time Taken (in minutes and seconds)
Java from Python 4m 43s
Python 4m 28s

Also, as you can see below the memory footprint is near identical too.

Java from Python :
pythonwithjava10000-crop

Python :
python10000-crop

I’ve included the actual Java memory usage in the top image just to show that it’s negligible as you’d expect.

So overall the indication is that if you need to use Java keywords it might be worthwhile thinking about using a library like py4j.

Any comments or suggestions are welcome.

Tricks for dealing with uncreatable classes under test in java

All the code I’ve described has been put in a library called Cut that you can find at https://github.com/devwithahammer/cut

As somebody who likes to follow an approximation of TDD, writing unit tests is always my default starting point when dealing with legacy code. Amongst the many problems posed by this, often the trickiest one is actually creating an instance of the class under test. Here are two simplistic examples of what I’m talking about.

public class Foo {
    private Resource resource;
 
    public Foo() {
        Resource resource = createResource(); //Under test circumstances this throws an Exception.
    }
}

public class Bar {
    private static Resource resource;

    static {
        resource = createResource(); //Under test circumstances this throws an Exception.
    }
}

Obviously, it will be impossible to create either of these classes using normal construction.

The purists will no doubt be suggesting that the best course of action is to do some minor refactoring first and you’ll find many good tutorials on how to do that on various TDD forums.

However, one of the problems with this approach is that is that you can end up trying to refactor some pretty horrendous God objects without the safety net of any unit tests. The lack of a ‘no-regression’ guarantee is pretty nerve jangling.

Besides, sometimes you just want to set up a test that excercises a certain bit of code quickly and worry about the actual regression test/refactoring later.

So..

Errors in constructors

Turns out this one is actually pretty easy thanks to the good people at Objensis. It’s as simple as

ObjenesisStd objenesis = new ObjenesisStd();
Foo instance = objenesis.newInstance(Foo.class);

Objensis is what Mockito uses under the covers, and it uses various native library techniques to create the objects in question.

Errors in static initialisation

There’s no such nice solution here. What’s more, refactoring static initialisation code is exponentially more nerve-racking than constructor code, especially where resource intialisation is involved.

There is, however, a work around of sorts. Using ASM you can create a class (as in a new type) that has all the same code as the class you want to test but without the static initialisation. In fact, thanks to the fact that ASM has a pretty intuitive interface it’s not actually that hard.

ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);

ClassVisitor = new ClassVisitor(Opcodes.ASM4, cw) {
    @Override
    public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
        if (!name.startsWith("<clinit>")) {
            return super.visitMethod(access, name, desc, signature, exceptions);
        }
        return null;
    }
}

String className = Foo.class.getName();
String classAsPath = className.replace('.', '/') + ".class";
InputStream in = Foo.class.getClassLoader().getResourceAsStream(classAsPath);

ClassReader classReader;
classReader = new ClassReader(in);
classReader.accept(cl, 0);
		
byte [] newFooBytes = cw.toByteArray();

Now all you need to do is write a class loader to turn those bytes into a loadable class and use Objensis to create an instance (or just use the newInstance() method on the class if it has a parameterless constructor).

What you need to do now is actually access the methods on the class you’ve just created. Obviously you could use reflections, but that’s not going to make for very readable tests and we can’t cast it to the original class because it’s a completely different type. The workaround is to use ASM again. What you do is create an interface that contains all the methods of the class with the static initialisation errors that you want to test and put that in the hierarchy of the class that you’ve created using ASM. It’s turns out that doing this in ASM is trivially easy.

So, say your class Foo has two methods that you want to test: methodA, methodB then you would create the following interface

interface TestFoo {
    MethodAReturnType methodA();
    MethodBReturnType methodB();
}

and then our class visitor becomes

ClassVisitor = new ClassVisitor(Opcodes.ASM4, cw) {

    @Override
    public void visit(int version, int access, String name, String signature, String superName, String [] interfaces){
		
        List<String> newInterfaces = new ArrayList<String>(Arrays.asList(interfaces));
        newInterfaces.add(TestFoo.class.getName().replace(".", "/"));
        interfaces = newInterfaces.toArray(new String[newInterfaces.size()]);

        super.visit(version, access, name, signature, superName, interfaces);
    }

    @Override
    public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
        if (!name.startsWith("<clinit>")) {
            return super.visitMethod(access, name, desc, signature, exceptions);
        }
        return null;
    }
}

Now the class that you’ve created can be cast to TestFoo and thus we can access methodA and methodB.

…Or just use Power Mock

When I starting writing Cut I didn’t realise that Power Mock allowed you to create objects that had static initialisation errors. They go through how in this post. Basically, you just need to add a @SuppressStaticInitializationFor annotation.

Although I haven’t looked into very deeply, it looks like they’re using CGLIB under the covers.

Any thoughts, comments or corrections are welcome.