Clean, declarative code
Which is easier to understand:
stand up, walk 10 steps forward, open door, walk 30 steps left, go down stairs, walk 50 steps more, turn left, open door, ...
fetch a gallon of milk from the store
Whereas Imperative code describes how the task is done, declarative code instead focuses on what the task is, and can drastically improve readability. One easy way to think about this is: code should read like a newspaper; headlines on the front page that succinctly summarize the content, and details continued on page 12.
Architecture, testing, and team efficiency all go hand-in-hand. When you have good architecture, it's defined by the boundaries you expect different types of objects to have, ideally defined simply as inputs and outputs. When that's the case, it's easy to write tests around your objects because it's already known what the inputs and expected outputs are going to be. Furthermore, when it's easy to write tests and it's clear where certain logic should live, your developers are able to work quickly and effectively.
My background is heavily in iOS, and Apple does much to lead developers astray from what MVC should look like in an app. MVVM(+C), VIPER, and RIBs are a huge step forward for view- and event-driven applications, but the principles behind them are applicable to all systems: keep objects small with well-defined boundaries.
There are a ridiculous amount of benefits to testing, particularly following TDD:
- It makes your code better. If you're writing tests for your code and realize that the test is difficult to write, then you change up the production code to make it simpler and easier to test. The end result is smaller, more focused classes with a single responsibility.
- It's easier to dive right back into work. Run your tests, start working on the one that failed. Easy.
- It's free documentation that's actually effective. Following rspec style tests, we describe the function, each context it operates under, and define the expected result. When a developer wants to know how a certain class behaves, it's easy to understand by reading through the spec.
- It keeps development velocity constant. Tests limit cognitive overhead when projects become complex. Without tests, developers have to understand the entire system well in order to carefully navigate changes so that tangential features aren't broken in the process. With tests however, you can focus on the small context at hand, making changes with confidence because you can rely on tests to catch unexpected changes to functionality.
- It makes you prefer functional programming. We understand that state is the root of all evil; its buildup incites bugs and complexity even in a single-threaded world, and now we're working with high concurrency. When we define our tests in terms of inputs and outputs, we're framing the problem in a functional manner by default, and are more likely to functional code.
Some believe in TDD, I believe strongly in a concept called "Do what the fuck you want-driven development". Write your code from the highest level of abstraction first. When you need to introduce a dependency to separate logic, write it out as an interface or dummy-implementation, and write it in the way that you want to use it from the caller's perspective. If you keep writing and realize it would be easier if you changed the API, you're 100% free to do so because all that's defined so far is the interface. In this way, you're always simply writing the code you want to write, instead of dealing with APIs that don't quite work how you want.
Of course, there's a balance to be found here. Some dependencies may need additional parameters or configuration, and by no means is this an excuse to write a blocking function for an asynchronous call because it's easiest to do so. However, you'll dramatically reduce the number of times that a dependency makes it more difficult write a feature than it should be.
You've defined a role that you're hiring for, so you understand the experience, knowledge, and skills required for a candidate to succeed, but what separates those who will fulfill the responsibilities from someone who will excel at the job? Here are a few things to look for:
Attitude above all. Shit happens; a good attitude is critical to get through the hard parts—where the real learning lies.
An ability to learn quickly. Roles and responsibilities change rapidly at a growing company; the best employees will be able to react to this dynamic environment and proactively fill in gaps as they appear, rather than waiting for problems to arise.
Self-awareness. Self-aware people know how to bring the best out of themselves because they can objectively understand their strengths and weaknesses, playing into their strengths and improving upon their weaknesses. And because they're aware of them, self-aware people are already working on their weaknesses before others have to point them out.
Some questions you might ask during an interview:
- Name a time where you were able to keep a positive attitude through a tough situation. What was the result?
- Tell me about a time when it was necessary to admit to others that you had made a mistake. How did you handle that?
- Tell me about a time where you were put into a new situation and had to learn how to swim quickly
- Ask them to talk about what they've had to learn at different jobs
- What are your strengths and weaknesses?
- Tell me about a time when you were convinced you were right about something but then came to change your mind
The right fit for the team
Put simply, you wouldn't put Steve Jobs on a team with Steve Jobs.
Free-spirited leaders need help from a detail-oriented teammate or things fall through the cracks. You need analytical folks to research and validate different ideas, but need dreamers to spark innovative ideas. And while communication is key, it's easy to talk without result when you don't have someone who's keen to put their head down and churn out some real work. Finding the right balance is paramount, and requires understanding the personalities on your team so that you can match up a candidate who fills in the gaps.
Additionally, hiring for culture fit cannot be overlooked. No, I'm not talking about "can I have a beer with this person", I'm talking about the values you uphold and the methodologies you follow. Each new hire either reinforces or dilutes the culture that you aim to create or uphold, there is no middle ground.
Give them what they want
Good engineers are in high demand, and companies are hard-pressed to make sure that a position at their company is enticing. Companies like Google and Facebook do this by offering ridiculously good compensation packages including a high salary, ample amount of very promising stock, food, high-end equipment, social events, laundry services, the list goes on.
But I was able to consistently beat out offers like these at a startup with almost no benefits by simply asking questions to understand what the candidate is looking for. Most times, they're more interested in career trajectory* than the cushy perks. So I'd ask a lot of questions to understand where they're trying to improve and grow, and start to put a plan together in which they could realize those goals with us. This does a few things that shoot your offer to the top of their list:
- It gives them confidence in the choice. There are few things that give humans more confidence than an ability to believe in a future in which they're better off.
- It shows that you care. By first taking the time to understand your candidates, it shows that you're focused primarily on helping them achieve their goals, and secondarily on achieving your own. Wouldn't you love to work with somebody like that?
- It establishes that the future is theirs to make, not ours; we're here to help, not drive the process.
In order for someone to be effective on a team, they must understand the goal and their place and responsibilities in achieving it. In order for a coworker to live up to your expectations, they need to understand what's expected of them. In order for them to work within the way your company does things and the tools you use, they need resources to learn from. Clear definitions on these three things will go a long way in minimizing the ramp-up time for a new hire.
Let's talk about trust.
The definition of trust that I've found most meaningful for teams is stolen directly from Stride Consulting:
we understand and truly believe
that everyone did the best job they could,
given what they knew at the time,
their skills and abilities,
the resources available,
and the situation at hand
What this means tangibly is that when one team member suggests an idea that others disagree with, the others work to understand and question the information that led the idea, rather than calling into question the team member and their abilities. This feeds directly into the lessons taught in The Five Dysfunctions of a Team.
The book, in a nutshell, says this: Picture a team who's been given a project, and are working out their approach. If members of a team do not trust that their thoughts will be heard and their opinions will be valued, they won't speak their mind. And if members aren't saying what's they're thinking, they won't be able to engage in constructive debate; rather, "peace" during meetings isn't coming from total agreeance, but from fear of conflict. When no constructive debate happens, nobody on the team is able to able to feel bought-in to the approach; it's never more than "someone else's idea". And when it's "someone else's idea", there's no accountability to be found; when things go wrong, everyone's pointing fingers. The end result of all of this is a team who's not paying attention to the collective results of the team; they're too busy watching out for their own self interest.
So when I talk about trust, I'm talking about psychological safety—creating a space in which people are safe to take intellectual risks. Trust in the sense that if somebody has an idea that they're not completely sure about, they're still confident enough to share it knowing that their teammates will first work to understand the idea completely, decipher the logical steps that led to their conclusion, and will either agree or debate the information and logic.
At no point will the abilities of team members be brought into question.
Bringing back The Five Dysfunctions of a Team, a great team is able to share their thoughts and ideas freely. Because of this, they're able to engage in meaningful debates, bringing together all the information from each person in the room to come up with the best solution. When each member put something into the decision, they're each feel personally invested and responsible for a good outcome. So when things go wrong, instead of pointing fingers, the team members are incentivized to help each other out. The end result is a team who is more focused on a good outcome rather than their own agenda.
So it's no wonder that, compared to those in the lowest quartile, people working in the top quartile of trust:
more energy at work
more likely to stay
Source: Zak, Paul J., Trust Factor: The Science of Creating High-Performance Companies, 2017
Just one more thought to wrap it all up:
New ideas are fragile - Jony Ive
Great teams take tiny, brittle, weird ideas and put them through the rock tumbler, turning them into great ideas after a period of nurturing, development, and constructive debate. But it's so damn easy to prematurely kill a great idea at any point along this journey. Psychological safety is critical if you want to bring out the best ideas from your entire team, rather than a small loud few.
In Radical Candor, Kim Scott tells the story of the greatest morale boost that Google has ever seen. Google had recently acquired a company, and a team of 700 along with it. These new arrivals were
...thoroughly demoralized, pessimistic about their growth opportunities at Google and convinced their managers didn't care about them. Very few reported that they expected to be working at Google in three years.
Russ Laraway, Director of Sales at the time, turned this around with a method he developed for having career conversations with direct reports. The process consisted of three 1-on-1 meetings. First, the life story—talking through important life decisions and events to understand what the person cares about and what motivates them. The second, dreams—to understand where the person is trying to go, personally and professionally; what they're trying to achieve. Lastly, a meeting to develop an 18-month plan in which that person can begin to achieve those goals, or develop the skills necessary to achieve them.
Russ's approach was so successful that an internal survey of employee satisfaction showed the people on his team displaying a marked increase in optimism about their futures at Google and their positive feelings about their managers. Nobody from HR had ever seen such an improvement.
This goes about as deep as it gets. Each person on your team has their own path in life; and each of them have somewhere that they're trying to get. If you are able to construct roles in which each person on the team is working towards their goals, then they will turn out to be the most productive team that you have ever experienced.
Empower with context*
Brilliant people make terrible decisions when they have incomplete information. George W. Bush was incredibly smart, and yet made poor decisions because he was working off of insufficient information.
At Chexology, our users frequently reported issues connecting to the bluetooth reader. The PM kicked off a meeting by presenting a feature to address the issue. There was nodding around the room, but no real feedback. This was a highly passionate and intelligent team, so the lack of discussion was a big red flag. I started to ask questions to establish context: what are the users saying? where are they getting stuck? what is the actual issue—does the device fail to connect or do they simply not know when they're connected?
When more information about the issue came to surface, the rest of the team finally started to chime in and work together towards a solution. When all was said and done, the energy in the room was through the roof. We had come up with a brand new solution together, one that every member of the team was confident in and excited to build, and when built, did the job better than we had even expected. The best part however, was that when the original proposed solution would have taken the team 2-3 weeks to build, the solution we went with took 2 days and was incredibly effective.
The bottom line: the more information and context you give to your team, the better they'll be able to make decisions and move forward as a company.