Your First Startup Software Team: A Primer
May 14, 2018
I've been on a number of smaller projects at 20-30 person companies, and there's always that awkward point where a product crosses over from "mostly important" to "very important." The team grows, and suddenly someone is in the command seat for technical product implementation. Whether that's a new hire, or a senior developer turned lead, it's usually a tough transition.
What follows is a "starter template" to running a larger project. I very much believe that product/project management has to grow with the product being built, and to how the company operates. In Agile Manifesto terms, "people over process." That doesn't mean you can't have process (as I'm about to lay out here), you just need to be calculated and reflect on it often. Tweak what you find here so it works for you.
Note: I work a lot with remote companies. The process here lends well toward doing work asynchronously, and externalizing the work to be done (rather than sharing it by word of mouth). This is good for in-person teams too – it breaks down knowledge silos!
Define, and understand, roles
Job titles and roles take on actual meaning as a company – and team – grows. They define authority/hierarchy and define a set of items on which your progress/effectiveness is gauged. They bring the security of understanding your set of duties, and make it more clear when to hand-off or hand-up an issue outside of the scope of your concerns.
Here's a quick rundown of what roles I've often seen. Even if you wear a few hats, I think it's important to recognize these – it'll help you delegate your roles over time to others:
- Team Lead - Probably you; responsible for the team and its technical outcomes. You pair with a Project Manager to meet deadlines and take on well-understood, and hopefully doable, work. Need to be comfortable saying "No" to unrealistic deadlines, and carry the weight of insulating developers from managerial pressures. You are only as effective as your team.
- Project Manager - A client-facing individual that becomes responsible for delivery deadlines. Somewhat overly-beholden to the client (i.e. often evaluated in terms of client praise), and consequentially not really a proponent of your own company's best interest. That's ok; as Team Lead, you are to counter-balance the needs of the client with what is achievable. A good project manager can manage client expectations as a result, and hopefully keep the team out of hot water.
- Product Manager - Not a client facing role! This person sees the future vision of software-as-a-service type
products. They're the guard against a single client running your entire product. They strategize for client
N+1
and are responsible for not letting a single client take the company for a ride. - Senior developer - A developer that understands that every line of code they write satisfies a business need. Will take dumb/boring solutions of complex/uncertain solutions. Knows to raise a red flag if a chosen approach is harder than the team thought.
- Junior developer - Someone just setting out in their career. Seek out people who love to learn and are good at it!
Here's something important: all of these roles are basically peers. The "Team Lead" may have extra authority over developers and make tie-breaking decisions, but that's it! It's easy to fall into a disorderly hierarchy (e.g. project manager running a technical team) if this isn't made clear early on.
Sitting atop these roles would be directors of respective departments, or C-level individuals.
Learn to delegate
The first step to running a team is learning when to hand off work. As a lead, it's tempting to take on the harder (and sometimes more interesting) technical work, leaving the boring "easy" stuff to other team members.
This, unfortunately, is counterproductive from a team perspective:
- Putting yourself on critical projects/technical pieces means that when higher-up meetings and other priorities come knocking, the team is often blocked by your silo'd off work that's happening.
- As a lead, your job is to facilitate everyone else's work. Taking on work of your own – especially technical work – all too often leads to spending far too much time / energy coding, and too little resurfacing for air to check on how team members are doing, and checking on overall project process.
If you find yourself apologizing to developers for other projects sapping your time, it's time to speak with superiors about the problem, and/or delegate even more.
Try delegating everything to start. See how little coding you can do. It's going to feel odd, but only once you know how to do it can you get back into code-writing. Odds are, your value lies in drawing on past development experience to help steer high level technical architecture / strategy, more so than grinding out the actual code.
Don't micromanage
If you have a technical background, you may be very tempted to peer into what others are doing and essentially code over their shoulder. This is one of the biggest lessons in delegation: don't micromanage what others are doing. Let them free enough to engage creativity and come up with solutions on their own. They will falter, sometimes making mistakes you've made before, and your goal is to foster their learning experience so they can organically – granted, with a bit of guidance – arrive at your level of experience. You want to develop their intuitive understanding, not necessarily just their objective what-they-are-told understanding. The key is to delegate small chunks that let that person learn, and give them opportunities to come to you for hints along the way.
Micromanagement may sometimes seem appealing – after all, you're "helping" that person to do something a better way. The problem is, this stamps out creativity on that person's part, and if done enough, will leave them resigned to letting you dictate every little thing they do. On a team, you want independents, not dependents!
At the outset of a technical feature, lend your expertise, and definitely step in if someone is blocked, but otherwise let that person explore their way to a solution without checking in on them constantly.
Above all, don't let pressing client obligations be your reason to peer into someone's progress. Client pressure has no place in a codebase, and is a purely human / project issue to be handled by non-developers.
Unblock others
With all that about micromanagement said, do monitor to see when team members are blocked on a problem. Some people won't feel comfortable raising a hand if they have a problem, or may be locked in that particular programmer mindset where "just a few more lines of code…" will unlock the issue they're solving. Watch out for team members blocked, and step in to help them unblock. That may mean pair programming, or just consulting with them using a whiteboard. Sometimes it's as simple as going on a walk to talk through the problem.
The goal is that developers should feel comfortable starting to recognize when they are blocked, and surfacing it to the team for help. There's a way to formalize this in process, as we'll see later.
This is the core function of "standups" – daily meetings to say what you're doing. It allows you to quickly spot someone stuck on the same problem for ~3 days in a row. A side note on effective standup: just have people post one in Slack! These aren't really necessary as in-person meetings.
As a team lead, this is chief amongst your priorities!
Timebox/budget where possible
Intentional use of time is the best guard against wasted time.
As a Team Lead, you should pre-allocate certain blocks of your day to certain activities. This means email, mentoring, meetings, coding, etc. all need to be budgeted. Since coding is timeboxed here, it's going to curb your natural inclination to become just another developer on the team, and refocus you on your other priorities.
Same goes for meetings; aggressively define their length, and ensure recurring meetings have a time limit. Consider implement "no meeting days" to give developers a chance to work in complete flow state!
This is also true of code review: allocate N minutes (45-60) a day to developer code review. Ensure developers use this time. Prompt code review is critical because it blocks units of work from delivering!
Flatten the team; share knowledge
In small projects, it's common to have the uber-employee who knows everything about a silo'd off part of the platform. From a developer perspective, this is nice: it's job security, and hides under the guise of "not being micromanaged." The problem is, from a business perspective, this is a huge risk: if that developer leaves or falls ill, then the project is seriously compromised.
There are situations where it's fine to have the uber-developer. If a company is ok with this risk, then using just one developer can sometimes add additional efficiency. The problem is that a team is seriously compromised by having this uber-developer – without that developer, it becomes ineffectual.
The simple way to mitigate this risk is to begin sharing knowledge between team members;
- In pairing; pair those who know everything with those who know little to implement projects. Sure, you'll slow down the one who knows everything, but you're paying a price here to spread knowledge around.
- In documentation; have those who know everything document what they know, then hand it off to the one who knows little, and see if it's enough to get them through their task. If not, rinse and repeat and have the more experienced developer share knowledge through documentation.
- In code review; juniors should be reviewing seniors and vice-versa. When someone less experienced reads new code, they learn. When someone more experienced reads a junior's code, they can point out issues based on their knowledge, and again share what they know. As a "senior" developer, I've learned new emerging design patterns that newer developers have brought to projects!
Execute the project in units of work
Whether it's Jira, Pivotal Tracker, Trello, Redmine, GitHub Issues, etc., get your work into a project management system.
You want to reduce everything you do to units of work. Call it "tickets", "issues", or whatever – but these:
- Need to have some notion of "sprints." Decide what you're going to do over the next 1-2 weeks.
- Need to be something you can estimate. Do so in terms of "points" representing some rough approximation of time. That scale depends on your needs, but the simplest start is working with 1, 2, or 3 points, and let a point be one day.
- Need to be something you can establish a blocking hierarchy with. As you explore functionality, you will discover prerequisite work, and shift focus to that. Once the pre-reqs are figured out, you can move on to what was blocked.
- Need to express "not yet ready to work on" ("Backlog"), "we have questions" ("Questions"), "ready to work on" ("Ready") statuses
This is often a tough transition for a team that may just have everyone committing to master
, or create very ad-hoc
pull requests. It's also tough for teams that are only superficially using project management tools – e.g. assigning
points but not really tracking per-sprint capacity.
Just like establishment of code standards and rigorous adherence to them, you need to establish project standards/workflows, and also adhere to them strictly. We'll discuss retrospectives later as a means of avoiding mindless following of rules.
Executing in units of work allows the team to build a workflow around putting small, incremental features in production. Working with indefinite chunks that may span days of development encourages developers to go dark, and progress to become uncertain. Pulling out units of work also allows the team to spread out the load of development, rather than centralizing all on one person.
Some developers will not necessarily be happy with this transition. This is due to a few reasons:
- Bad implementations of "Agile" leave a bad taste. e.g. project managers may end up playing team lead and pressuring developers directly. Or, a technical leader may use team meetings to dictate how development is done technically, essentially micromanaging (even I've done this before!).
- Perception of decreased autonomy. This one is somewhat a fair point: team-based
development can't easily tolerate super-autonomous in-the-dark developers who are executing essentially
dozens of team tickets in the dark. Accountability to the team
is important, and it's completely admissible that this does make developers more a plug-n-play asset.
This is one of the core trade-offs to executing work as a team: business risk is reduced in that
developer leaving or becoming unavailable, but at the cost of individual developer autonomy.
- The way to prevent this from becoming an issue is to keep your tickets very high level; don't dictate technical approaches, but leave it up to the developer. Leave room to explore and learn.
- Believing architecting is being sabotaged. This one is also fair: when you execute in small units of work, you may end up with more junior developers making off-the-cuff data modeling decisions that more experienced developers may not agree with. But, here's the antidote: code review gives those more experienced developers a voice, and if an area is particularly architecting-heavy, the team can execute "spikes" to explore, contrast, and document architectural approaches before putting them into code.
Use retrospectives to draw out opinions, even the negative ones. Discuss as a team, and seek to adjust process to remedy those concerns.
Executing units of work
Now, off to the Agile races! Meet daily, weekly, or whatever cadence produces a few good chunks of work. As a team (that means the Team Lead, Project Manager, Product Manager [if applicable], and developers), everyone should go through the "Backlog" issues in this sprint, and convert them to "Ready" issues.
This is certainly its own topic, so read on here: Anatomy of a Good Software Ticket & Workflow.
Know the pitfalls of "project management"
- Meaningless sprints - In companies where client obligations rule-all, sprints often degrade into meaning "the set of things we're probably, maybe doing this week." The current sprint regularly has items snowball into it, only to be arbitrarily pushed out later. Often, the "current sprint" is being actively converted from Backlog -> Ready tickets on the fly, rather than being ready for developers in advance! This is a sign of an organizational problem – usually the client running the show on an under-resourced team. The only way to get things back under control is to start tracking "sprint slip" (how many tickets are being pushed off per sprint? what new tickets are being injected on the fly?) to appreciate the origin of the problem, and then setup rules that dictate how tickets are pushed from (or put in to) the current sprint. The goal is to force more intentional decision-making on what enters and leaves the current sprint. It's fine for a certain slippage due to emergency bugs (and it should be factored into acceptable sprint-size-in-points), but if work has to be pushed off (or added last-minute), someone in management (and/or the client) eventually needs to sign off. You may find that directors / C-levels need to come in to help here; clearly fame the problem and offer solutions, but make it clear you need them to enact their authority to back up the proposed solution.
- Process for the sake of process - It's easy to forget to reflect on process, and just carry it out mindlessly. Use retrospectives to give the team a chance to point out issues you may not see. Even the process you're reading about here will need tweaking for your company. Think of process like its own software project, but instead of code it's documentation & workflow diagram(s).
Implement 1 on 1s
This unpacks into its own topic, but the first step to meaningfully helping people on your team is to implement 1 on 1s between them and someone who has authority over their direction in the company. If you aren't that person, you might sit in on those meetings if appropriate. Give members an avenue for career growth, and a forum for their career concerns.
Give them an avenue for voicing concerns 1 on 1 at the team level too. This is particularly valuable to receive in-confidence feedback on your own performance! Also use it as a means to help them achieve their career goals as discussed in career-oriented 1 on 1s.
In both cases, it's a good chance to connect with that person on a deeper level. Keep it casual – go out for lunch/coffee, and don't focus on work too much. It's completely fine to discuss the latest tech, hobby, etc. you both share an interest in. Build a professional, human connection! This is also a chance to help out if someone's work performance is dropping – don't pry, but do try to understand personal obstacles that may be affecting them professionally if they're willing to volunteer them.
Build team spirit / professionalism, even in code review
It's hard to describe, but the most effective teams I've been on have a "get it done" attitude, paired with a healthy amount of mutual respect. That doesn't mean working long weeks, high-fiving every pull request, or committing spaghetti code as fast as possible to meet deadlines, but it does mean being willing to give frank and constructive criticism.
This type of team is built of professionals who care that the job gets done right, and won't be afraid to speak out if they see an issue (e.g. in code reviews, calling out more senior members on areas of concern). They understand that it's important to get along, and their feedback is framed:
- Positively - Keep in mind the "we're all in it together" mindset. Team members should leave constructive, level-headed criticism.
- With room for rebuff - One of the best techniques for calling out issues is to reframe your concern as a question. Sometimes, you see a technical approach that looks rediculous. Call it into question rather than advising against it. Let the other developer consider and defend their position – you may be surprised to find that they are on the right path, or wisely chose to avoid a time-sink rabbit-hole in favor of a simpler, somewhat-sub-par solution!
In a high-functioning team, leaving less-than-stellar code review feedback, or even challenging the technical approach of another, is just seen as part of the job. It's not personal, it's professional.
Implement high-level retrospectives
Schedule retrospective meetings every 2-4 weeks. The goal is to reflect on project process (workflow, how tickets are written, exploring whether sprints are effectual, exploring whether the points scale is still applicable, etc.), as well as technical standards (should standards be amended? is pair programming working, or does it need tweaks? is there a recurring technical theme sapping developer time that could be worked on?).
This is the chance for unhappy team members to have a voice, and for you to seriously consider that voice – often with the aid of the team. There's often a drastically different perception of a project between its leaders and its implementors. Things you may be ignoring or consider to be small problems may be large hindrances for those doing development. This may not just slow them down, but may bring on unhappiness that saps creativity and job enjoyment.
The core goal of any retrospective is intentionality. Are your processes actually serving you well? Is that one thing you've been doing a year still worth doing? Be sure to use the fresh perspective of new team members, and let them know it's ok to question your process – can the team defend its process to outsiders?
Build an onboarding process
As the team grows, you need a clean onboarding process. It can be simply a short checklist, but you need something. One of the most frustrating companies to join is one that doesn't give you clear direction from day 1.
As a starter,
- Always have a few simple issues the developer can begin on that are already "Ready" for them. Let them start contributing right away, and make it clear there are no time constraints for their implementation. Let them submit their issues as any other developer would.
- Have them in team meetings for preparing units of work right away. Let them get a feel for the process. They don't have to contribute much early on, but encourage them to ask questions, even about the obvious things. The team should be prepared to pay a small price during these meetings in time necessary to catch that developer up.
- Pair that developer with other experienced developers. This is a cost to your experienced developers in time, but is an effective way to train-up the new developer on team process / code standards.
I often see onboarding omitted entirely. This is because it's seen as a cost, which it is. When considering a new hire, remember the cost to the team in training them and preparing documentation for them!
Set code standards, enforce rigorously
One of the more frustrating experiences is being on a team without coding standards. Everyone has their own idea of Rails best-practices, but they're all a bit different.
Start a wiki, and document what you intend to enforce. Be petty; call out indentation issues in code review, and make sure you understand the goal of any new code patterns that arise (e.g. the Rails Service pattern is well-known, but implemented in quite a number of ways, regardless!)
Measured conformity is valuable:
- It lowers cognitive overhead; if the team uses a fixed set of predictable design patterns, then they become second nature to read
- It forces the team to collectively decide on what's a good idea
- It gives juniors a quick way to ramp-up on what it is you want to see from them code-wise
Flattening means that you, too, are essentially a (somewhat empowered) peer to other developers. Your code is
subject to team review just like everyone else's! Avoid comitting directly to your main branch (e.g. master
) – you'll trod on / conflict
with the work of others (very frustrating), and you probably won't know it.
Conclusion
This is by no means a comprehensive guide, and in retrospect it's probably better written across several articles, but I hope this gives you a starting template to running a team – whether Rails or other technology!
Feel free to reach out if you have any questions.