вторник, 20 декабря 2016 г.

Interviewing Developers: triggers, part III

The series:
Interviewing Developers
Interviewing Developers: triggers, part I
Interviewing Developers: triggers, part II
Interviewing Developers: triggers, part III

This post also appears in my other blog.

In the two previous posts I focused on certain behaviors of an interviewee, which may indicate problems with their ability to become a good developer in my team. I covered quite a lot of such aspects that push me towards saying "no" and now it's time to show that I can be positive as well. Positive triggers also help conduct an interview, although rarely shorten it, because they make you eager to speak more to the person. In this post I will list some things that make a good impression on an interviewer, explaining why they are good and thus showing what I look for in potential hires.

Candidate: "If you're interested, I'd like to explain the broader context of my previous job / project / task"

Just basic politeness and showing awareness of the fact that the interviewer may have their own plan and time limits change a lot. However, combined with a person' willingness to share his or her specific past experience it is even more powerful. In many cases an interviewer has to spend a lot of energy to get a reasonable picture of the candidate's background and has few options other than squeezing the bits of information out of the person, so one who offers a clear explanation themselves makes a head start. This positive impression may be further amplified with occasional dives into technical details - for example, down to a brief explanation of a class hierarchy - because this likely indicates both an ability to communicate efficiently and a desire to dig into things and understand them. Finally, note the initiative - sometimes it might be the most important aspect of a future employee - it means they have ideas and fuel to make them happen and your organization will only have to stir them in the right direction.

Candidate: "So we stick that thing into that stuff and …"

This one may sound unprofessional and stupid, but trust me, an ability to explain complex technical constructs using the simplest common language terms (yes, I qualify "stuff" for a term) usually indicates that the person does understand the solution being explained very deeply. Of course, whether you understand what they are telling or not makes a lot of difference and I speak only of the case when that explanation is comprehensible. If it is, you will likely find that it is easy to discuss both the edge cases of the same problem and similar but less familiar issues with the developer. That happens because a person using less formal terms in their speech has already internalized the knowledge being communicated - not just remembered some bits about it - and can play with his or her mental model to adjust and adapt it for other scenarios. This trait of a powerful mind is something that you would terribly like your teammates - especially, developers - to have.

Candidate: "Polymorphism is <reasonable explanation>. Imagine we have a base class called Person…"

Bless the people who provide examples for abstract ideas without being asked directly! Examples help every party involved in communication understand the topic better and spot new questions and ideas. While this merely makes the task of answering an interview question easier to do right, the real value is that the person shows that they are able to see the implications of an idea and view it from different angles. A developer who quickly comes up with examples and tries to view a problem or idea in concrete figures usually thinks realistically and understands the context better. Such a person quickly grasps the real world problems that a piece of software tries to fix and can transfer this understanding to their colleagues. As for an interview, they try to make it easier for you through their usage of examples and this alone deserves some respect.

Candidate: "We were using ASP MVC 2, which is kind of old, so I decided to learn Node.js and built a personal web-site / game / whatever with it"

Here I barely need to explain anything: desire to learn and develop on one's own is what makes great professionals. They also don't blame their boss or organization for not giving them an opportunity to learn new things or work with the hottest new technologies - they simply learn whatever they find appropriate when they feel a desire to do so. On the other side, any developer may occasionally get a bit tired with their day job and issues associated with it. A person with a positive attitude feeling their own responsibility for their growth will be able to help themselves through such uneasiness by means of a personal project. It is true that such a side activity may distract someone from their job, but the stakes are high it would rather improve the way the developer attends to their duty giving them extra energy and ability to bring new ideas from the outside world.

This is far from an extensive list of positive interview triggers and there are dozens of things that will make an interviewer like a candidate. Still, I will stop right here, because the things said are enough to give a good indication of what I value when sitting on the hiring side. I must have highlighted this a thousand times, but any hiring decision is based on a lot of factors and no single one of the triggers that I mention should be treated as a basis for yes or no. Still if you have a collection of such triggers in mind they may help a lot navigating you through the process of acquiring a new team member.

An interview is about finding a great person to close a gap in your team, so it is important to note both positive and negative edges of the candidate's personality. The goal is to check whether the candidate is a good fit for the open position, the team and your company's culture - that's why I pay that much attention to the communication abilities and personal traits. Of course, the most important purpose of the interviewing process is to avoid hiring the wrong person - hence the relatively large number of negative triggers in the previous posts versus few positive ones in this one. At the same time, noticing the good aspects is also important - especially when these show that after being hired the candidate will keep growing professionally, help others and improve the way they and their new team do the job.

In conclusion of this short series I wish you good interviews - on both sides of the process - and welcome you to share the bits that help you learn more from them!

четверг, 8 декабря 2016 г.

Inerviewing Developers: triggers, part II

The series:
Interviewing Developers
Interviewing Developers: triggers, part I
Interviewing Developers: triggers, part II
Interviewing Developers: triggers, part III

This post also appears in my other blog.

Last time we looked into several responses that I don’t like to hear during an interview. In this post I will add more examples of candidates’ phrases, which usually indicate weak potential for becoming a worthy team member. Next time, to finish on a good note, I will switch to positive interview triggers, but today let’s keep a bit negative.

Candidate: “I’m tired of writing code. I want to be an architect in your team.”

The funniest thing about this is that I usually hear it from 23–24 years old guys, who have at most a couple years of commercial development experience. It is clear that a candidate with this kind of background is hardly fit for a software architect’s position, but it is also difficult to consider them as a developer because they admit they don’t want to do programming anymore. What’s worse, I believe there are few (if any) good architects who don’t wish they could spend more time coding. Somehow the love for programming comes together with good software design skills and the lack of it means one can’t go far on the technical side of the software development world. The simplest rule here is: never hire for a knowledge-based position someone who doesn’t like its core — they will likely be both inefficient and dissatisfied.

Candidate: “That wasn’t my responsibility”

Usually this comes in response to questions like “Why did your team chose to implement X and not Y?” and “Why did you use that framework instead of this one?” If we speak of junior and mid-level developers we indeed must assume that someone else was in charge of making both product and technical decisions, but it is also true that good developers usually care enough about their work and team. This means they do question the decisions made by the team or the company and try to understand the reasoning behind them instead of blindly doing what they are told. It is also common for them to say “we” about the team — even after they left it — and assume some responsibility for its results. If a person does otherwise, it may indicate a lack of curiosity and ambition, both of which are crucial to becoming an effective team-player and growing one’s skills with time.
Candidate: “I was good but the other developer made incorrect decisions and so we failed”
This one is a refined version of unwillingness to take any responsibility — particularly, for failures. I bet, when asked about the reasons behind one of their successful projects that same person will claim full credit for that success. The problem is that such a person is likely a poor team-player. Clearly separating oneself from their team instead of saying “we” tells a lot about the person’s ability to make the team more effective, collaborate and help others. I definitely don’t want to hire someone who will do poor job and then blame the whole world in their failure — such people learn little, don’t work very hard and thus rarely succeed. Moreover, blaming others doesn’t make you look reliable and trustworthy — even outside the interviews.

Interviewer: “Which of your recent achievements make you feel proud?”
Candidate: “There wasn’t anything like that — just common work.”

While treating this response in a strictly negative way might feel too much, it is nevertheless very informative. Even with purely routine work a good employee will discover a way to optimize his day-to-day duties and will feel proud of doing so. The same way, even people who truly believe that they are inferior to others and the results of their work are flawed will likely find something to be proud of if they are willing to assess their achievements. Conversely, candidate’s inability to identify something worthy among their results means they are unprofessional, because a professional always reflects on what they do, looks for ways to improve that and definitely has a thing or two to name as their valuable accomplishments. You don’t want to deal with someone who shows little initiative and lacks a purpose, do you?

In these two posts I tried to outline possible characteristics of a person I wouldn’t want to hire. Like I said before, neither of the triggers mentioned in this or previous post can make me reject the candidate — they only give me a hint on what to look for and which skills and aspects of personality to analyze. It helps spend less time on interviews and also allows me to maintain a clearer understanding of the traits that I expect and don’t expect from the great developers, QA engineers and analysts we are looking for. I promise to return with a more positive attitude in the next post and review the behaviors that make candidates look good to me.

четверг, 24 ноября 2016 г.

Intervieweing Developers: triggers, part I


Recently I explained the key principles that I follow when interviewing developers, but didn't cover an important aspect - the triggers that may significantly influence the flow of the interview. The trick is the more you do interviews, the better you deduce the character of a candidate from some of their behaviors. In particular, I certainly learned to notice particular responses that the candidates we don't hire may give. Even though one can't base the final decision on a single phrase, these triggers may both save some time and allow to avoid fatal mistakes. To begin with, here are 4 phrases that I heard from some developers.

Candidate: "Normally I work in IDE, so I can't solve this exercise on paper"

I've seen this one several times and it always came as a surprise. Yes, we love paper-programming! We ask quite some practice questions, but hardly ever give the candidate a laptop. I acknowledge that developers rely a lot on the comfort of IntelliSense, ReSharper and ability to run the code and tests that a laptop with an IDE provides. At the same time, offering to do an exercise on paper we want to check certain things about the candidate and refusal to undergo this check is what brings me closer to rejecting.

The idea behind coding without an IDE and compiler is that it allows us to see how a programmer makes assumptions about their task and tools and acts under a healthy portion of uncertainty. In other words, paper-programming focuses more on how you think and approach programming, than on your ability to brute-force the solution by trial and error. I interpret such a dependency on one's tools as unwillingness  to think and work under unfamiliar conditions, which for me means being unable to adapt to temporary difficulties and generally far from what we call a professional.

Candidate: "It doesn't matter that I can't solve the dumb programming exercise - I am good at real problems!"

Here it is dead simple: no, you aren't. Not unless you can show that and the "dumb" exercises are here exactly to allow you to do that. The purpose of sample problems is to verify certain skills. that we expect our potential employee to possess, and to see what kind of mistakes they make. If a person fails at this it doesn't mean we won't hire them - in many cases we spot what we need even behind a failure. But if someone tries to sell us the idea that, while they can't solve our mostly simple tasks, there are some mythic problems that they address easily, that is a sign of a phony, who speaks a lot, but does little. I don't think that loud yet empty bragging is characteristic of a great developer, be they junior or senior, - I believe they either succeed at a task or accept the failure and use the discussion to analyze their solution and spot mistakes there.

Candidate: "I don’t remember the theory - it's only practical considerations that are important"

It's not that our job is about reciting some "Introduction to OOP" book to each other, but it certainly hurts when people respond this way to a question like "what polymorphism stands for?". I expect some respect for the theory simply because it helps do our work better choosing the most suitable solutions and gives us a language to communicate with each other. Because the questions I ask are quite basic, those who fail to answer may have difficulties understanding software requirements and commit stupid mistakes. I'm pretty sure that someone who doesn't remember the key principles of OOP or can't explain the internals of a hash-table from the top of their mind can do good as a developer, but not when they denounce knowledge. Here, in addition to an attempt to gain my confidence by empty talk, I see reluctance to learn, which means I would have to invest a lot of time and effort into bringing the person up to the team's pace and establishing communication with them - the time and effort that I likely won't have.

Interviewer: "Could you please elaborate the answer and explain your reasoning?"
Candidate: "I just think that's the correct answer"
Interviewer: "What makes you think so?"
Candidate: "I just think so"
I was very surprised when I had this dialogue for the first time, but it was real and happened to me more than once since then. Sometimes I meet people who just come across a thing that looks like an answer to the question and try to sell it to me as though that's simply the answer that I need. As I must have repeated several times already, I am looking at the person's reasoning and striving to understand them better - not just trying to check whether they are able to guess a number. Even though it makes me feel somewhat stupid, it's not even the fact that this dialogue comes after I give some hints to indicate that the answer is incorrect that is most annoying. The problem is that such attitude shows that the candidate is bad at explaining their solutions and will hardly be able to contribute to a discussion of a complex problem with their ideas, not to speak of guiding other programmers and any kind of seniority. Even worse, like all the phrases above, this one is a sign of refusal to exercise one's brans - a very unlucky feature for a developer.

As you can see, these triggers show something about the person's approach to solving problems - not about which technologies they know or what tools they mastered. I pay that much attention to this aspect because it is relatively easy to teach a curious and thoughtful programmer a new language or a nice trick, but way more difficult and unrewarding to attempt raising a good developer from a person who doesn't want to study and sometimes even to think.

Even though hearing any of these phrases doesn't mean I would instantly stop the interview and refuse the candidate, they greatly impact the outcome of the interview. I have seen other managers hiring people who failed to solve an exercise or answer a couple questions and did that myself too. Still, I haven't yet met a person who behaved in the manner described above during their interview, but gave enough reasons to hire them nevertheless.

There are a couple other ways in which a candidate can draw my attention - both in positive and negative ways. I will get back to these in future posts. In the meantime, what do you think of these triggers? Do you believe they are valid or should interviewers be more forgiving?


вторник, 25 октября 2016 г.

Interviewing Developers


Our company has been experiencing rapid growth during the recent years, which means we were hiring a lot. Managing one of our teams I took my part in the process and conducted a significant number of interviews. When I first faced the task of evaluating a candidate for the software engineer position a couple years ago it felt very awkward and difficult, but over time I developed a general approach that helps me go through the interviews easily and quite efficiently.

Of course the key point about the interview is which questions to ask. In the beginning my attempts to come up with relevant and challenging ones were very painful. I struggled a lot and jumped from one topic to the other always being sure that I miss something important. However, when we speak of technical knowledge and skills, it turns out that the most fruitful questions to ask are quite simple. For example, I usually focus on basic textbook-level Object-Oriented Programming and C# topics. Keeping things simple you may quickly see that the candidate demonstrates only shallow knowledge and, which is more important, inability to explain technical stuff in plain and simple words with reasonable examples. Such an outcome means an end to an interview - if a person can't discuss basics they likely won't be able to reason clearly about the complex stuff that your team deals with. On the other side, the potential employee may show great ability to articulate the subject and its practical implications in such a way that you see the experience behind their words and learn quickly that you're not wasting time with them. In this case you may proceed with more complex problems, but this is rarely necessary unless you're looking for some very specific skills. 

Simple practice exercise have the same advantages and provide enough insight into the way the candidate reasons and knows his tools. Even the easiest programming problems usually give enough room for mistakes and bugs and allow to get a good evaluation of a programmer's skills. It may be a good idea to challenge the candidate with some really serious coding puzzle, but you definitely don't want to start with that. Because the technical interview is to large extent about assessing candidate's abilities to analyze problems and explain solutions we also always offer a logic problem. Being an exercise suitable for the 5th grade or something like that, it still tells a lot about the way a person would analyze the situation and draw conclusions, not to speak of their explanation skills.

Still, you don't want you candidates to spend the whole interview fighting boolean algebra, so you will need some reasonable practice problems. One good place to look for these is the domain that your software models. It is usually enough to spend a couple minutes thinking of what you do on duty to come up with a couple great yet moderately difficult problems. Just keep in mind that these should not require too much context or be too large, so that you can stick them into the timeframe of the interview. For instance, my list of practice problems includes an SQL question that I faced when developing one of our features - it is a pretty real-life yet very simple and thought provoking problem.

While technical questions usually take the most of my interview time, I also dig into the personal experience and attitude. Our HR talks to every candidate before I get to them, but I still find it very important to discuss the potential employee's background and soft skills. Anyway, I have to check whether the person will fit into the team and will be able and willing to do a good job. In additional to usual questions on their duties on the last job I love to ask for a couple achievements, which they feel proud of - people are frequently taken aback and start to dig for their best, which makes the answer very valuable. In addition to unveiling the candidate's experience, such questions allow to assess personal traits. For instance, I can learn a lot about the person's involvement into their job - good candidate would usually be able and happy to speak about their duties and achievements, explain them in depth and do it in such a way that I will get interested even with little context. The way a candidate speaks of his tasks and situation in the office also tells a lot about their attitude and motivation and may uncover teamwork-related risks.

To ensure that both technical and non-technical questions are informative and easy to handle I have them written out carefully and usually ask the same ones, mostly in the same order. Even though this might not seem important, it plays a great role as it prevents hesitation and fears of forgetting something crucial. Additionally, if I follow more or less the same path through the interview, it becomes easier to utilize past experience to spot patterns in the way candidates respond and dive deeper into certain subjects. Finally, this helps to compare candidates to each other, which is quite hard otherwise.

Another aspect of conducting interview, that I find very important, is to be kind. When I am open, attentive and helpful I can definitely learn more about a person than if I demonstrate them my superiority or show that I want to get away as fast as possible. A candidate in an interview usually puts enough emotional pressure on themselves, so there is no need to introduce any artificial stress. This helps not only throughout the interview, but after it as well: I may decide to make an offer and in this case I'd better have the person willing to work with me - not being frightened to death.

Thanks to all these tiny how-to's I got much better at doing interviews, but I still face certain issues. In particular, sometimes I pay too much attention to the person's communication skills. While their ability to speak and listen means a lot and helps both integrate them into the team and teach whatever is important to our business, there are many valuable hires in the world of programming who might be a bit tough to talk to. I also tend to try too hard to find pros about the candidate - in particular when it is already obvious that I won't hire or recommend them. I constantly fight this trait, but it requires a degree of discipline to cut off all of my willingness to understand the person better and say a firm "no" quickly. One inevitable consequence is that my interviews take a lot of time, but at least I know how to shorten them.

Still, the most unpleasant problem about interviewing and hiring may come in the process of making a hiring decision. It can be really tough when I think good of the candidate, but at the same time have a feeling that something's wrong with them. In most cases such sensation means no offer - even though I can't explain what bothers me, the hesitation is a good indicator of hidden problems, which may surface later. In spite of knowing the general rule "doubt = no hire", I sometimes have a hard time making the "no" decision and need to work on that too.

Despite these issues, the simple practices outlined above, like having a regular question list and focusing on candidates' reasoning abilities allowed me to become more efficient and confident on the interviewing side. In particular, this helped to hire several great team-members who have already delivered great results and became valuable employees. Overall, interviewing is not too difficult a process, but it takes some time to get it right. I'm still actively working on improving in this area, so please share your insights and how-to's!

понедельник, 26 сентября 2016 г.

The Inmates Are Running the Asylum

I recently finished reading The Inmates Are Running an Asylum book by Alan Cooper, which dives deeply into the question of how software design should be performed. There are great many interesting ideas and a lot of thoughts that anyone working in a software company may want to consider. Even though I can't agree with the views of the book to the point, I liked it and will try to cover both the thoughts that fascinated me and those that seemed questionable.

At the core of the book lies a simple idea that when we are going to manufacture something - possibly that's a piece of software - we should design it upfront and do that properly. Design at its core means establishing the overall structure of the product mainly from the point of interactions with users - down to UI design, but well beyond it. The term 'properly' is an equally sophisticated thing and expands to many different recommendations, warnings and tools. Some that stand out are: finish working on the design before you start coding; detach designers from coders; use certain techniques to channel design and limit the scope effectively.

The idea that we should put some effort into design before development can hardly surprise anyone. Even those who preach that a programmer should just hack the code till the product is born would likely agree that certain amount of careful and structured thinking has to precede creation of the first lines of code. The explicitly stated suggestion that coders should not do design and even that the designers should better be somewhat isolated from those who program is more controversial. Still, it makes a lot of sense, because programmers (and I speak of myself in particular) tend to focus on implementation details and reduce the domain to the level of their apparent technical capabilities. This strongly affects the point of view and ability to see what we are going to build, for whom and why - we mostly pay attention to how it is going to be built. At the same time, software should be designed not just to be buildable, but to deliver value in an easy to use manner. Our ability to provide value to the users is usually limited by poor understanding of their needs - that's something that most of the programmers faced at least once during their career. With usability we fail in a more subtle way - we make the thing easy to use, but only for the folks that look like ourselves, because that's what our experience and customs drive us to. In most cases, however, the users are people with a very different background, so we unwillingly make sure that they have hard time with our product. While these claims may raise some disagreement, they look valid, even though are not always the best to follow from the practical point of view.

At the same time, I couldn't agree with denouncing of the act of prototyping. The reasoning for claiming it bad is that prototypes tend to stick and in many cases to become the first version and the core of the final product, being least suitable for that due to their nature. That happens because programmers don't like to throw their creations away and managers, avoiding what looks like wasted effort, like it even less. While it definitely can be very hard to get rid of the code that is already written, I still don't think that the technique of prototyping a part of the solution should be disposed of. We should simply use it with care and be ready to dump weeks of our work into a waste bin because the goal of a prototype is not to serve as an actual basement for the final version, but to provide grounds for decision making that brings us to the finished product.

What makes the book very practical and valuable is the design tool that Alan offers - personas. The idea boils down to making up a narrow set of characters that depict future users of the product and allowing these fictional dudes to wholly drive the design process. Interestingly, the book suggests that the design team should give each persona a name and photo, assign user roles and thoroughly describe their background. Such characters would allow to speak of the product in terms of the needs of concrete, although unreal, people who are expected to use it. I must admit that I have never tried this approach, but it seems very powerful to me, because it gives a framework to ask proper questions about the thing in the making and to ensure that design stays on the right track, as we develop the product exclusively for our personas. This means that if there is no persona that needs a particularly fancy button, the button will never make it into the final design. The book goes even further with this suggesting that in most cases it is not right to subside to the customer's attempts to squeeze another feature from the development team unless our personas justify it. This idea rests on the assumption that a good design based on the right set of characters would cover the needs of the user, while anything that is injected into the product in spite of the design will give birth only to inconsistencies that will eventually ruin the software. Sure, rejecting client's request is a tough thing to do, but there is something to the idea of declaring clearly what your user looks like and staying by that declaration - it could certainly make some of our creations more consistent.

While I liked most of the core ideas in the book, it still raises concerns in regards to its practical applicability. The problem with the entire design framework presented by Alan Cooper is that it requires great upfront costs with the possibility of getting nothing but experience as an outcome. The biggest share of these costs comes in the form of the time spent without delivering any kind of a marketable product. In the modern world, when everyone strives to squeeze time to market as much as possible, it is difficult to take a path that promises to double it (in the best case). It also seems nearly impossible to incorporate this design and development methodology into an existing process - it can work great from the beginning, but when there are established constraints of an existing product, anyone trying to inject a serious design into a working motor will likely fail.

Overall, I definitely recommend this great practical book to anyone working in the software industry, as it gave me a new perspective into the development process and explained some very useful tools like personas. It also conveys greatly the idea that usability is born - or buried - deep under the hood of any software and can't be just added on top of a finished product, which is something that you want to keep in mind. Finally, a ton of examples - both related to software and beyond the industry - show that many things that we use nowadays are designed poorly and are still hard to use, which means there are lots of opportunities for making the world a better place.

понедельник, 29 августа 2016 г.

Developers on Watch

In a software development company that works for external customers it is common to spend significant amount of time dealing with external issue reports. While being tedious, this activity still conceals great potential for the development team's efficiency improvement and professional growth. At the same time this process is frequently organized in such a way that it never allows to get these benefits. In particular, that was the case in our team not so long ago, but we were able to change this for good.



Developing the ERP product we do face lots of support requests from our customers, some of which get escalated to development and turn into bug reports or new functionality proposals. Originally, there was a single person responsible for monitoring the queue of such items in each development team - the team leader. He would constantly keep an eye on the list of new bugs, analyze them one by one, which sometimes means debugging, comment them to provide first clues to the reporters and ensure that they are handled in a timely manner.

This approach has apparent problems, the most obvious of which is the lack of resilience. When the team leader is suddenly out of office, it is difficult for the team to take charge of the incoming requests because no one is used to the process. And even when the lead's absence is planned certain effort is required to ensure that someone on the team knows what to do about the queue and understands his responsibility for that - again, because the team members are not familair with the activity. This approach also puts significant load on the team leader and reduces his availability for other tasks - both organizational and development - which is rarely what we want. At the same time, when he actually has to do other job, fresh high-priority issues may be assigned to other developers and disrupt their plans in a chaotic manner. 

To get the idea of a better organization of this process it is worthwhile to examine the things that constitute it. Handling of any external request always starts with analysis of its validity, asking for additional information from the reporter, search for similar requests in the issue tracker or forwarding the problem to another team responsible for it. Subsequent analysis may require debugging and playing with the application and is usually aimed at both tracing down the root of the problem and providing a workaround or hotfix if it is required. In some cases one may produce the actual fix for the problem in the code - usually that happens either when the fix is simple or if the issue is very urgent (bad motivation, but that's another story). Frequently, though, the process ends with consolidating all the obtained information in the issue report and dispatching it to the most appropriate developer to be fixed sooner or later. The common feature of all of the above tasks - except possibly choosing the right assignee and implementing the actual fix - is that they can be done by any developer. Combined with the problems outlined above this idea brings us to a reasonable alternative implementation.

Instead of forcing all the activities associated with analysis and distribution of the new issue reports on one person it is way more efficient to evenly distribute this activity between all the available developers. The easiest way to organize this is to have them take turns so that each week one developer is fully responsible for managing the queue and has very few or no other tasks - in our team we call it Watch. While this process does require some degree of organization, it is not very difficult to put together. One has to carefully maintain the sequence of watchers and ensure that each team-member knows when it's time for him or her to go on duty. It is also crucial - especially at the beginning - to produce a piece of internal documentation explaining what processing a new issue report means and conveying the objectives of the process clearly. Major goal here is to encourage communication between the person who handles the requests and all the other team-members, such as product managers, other developers and support - it is vitally important for proper analysis of issues and for making the right choice of the way to handle them. Finally, to make sure that everyone who goes on Watch is efficient at it, someone should monitor quality of the results - this can be done by means of a regular review of several random closed cases with each developer or through any similar process.

While, unlike the original approach where one person is fully and permanently responsible for the job of handling incoming requests, the Watch process requires a bit more effort to establish and maintain, its enormous benefits to the entire team fully justify the cost. Most obviously, it enables better load balancing and - through explicitly securing one developer for unplanned activities - better planning. In addition to that it makes the team more resilient improving distribution of knowledge on different aspects of the product between team-members. This is especially helpful when there is a degree of specialization in the team. Importantly for support and customers, such organization also decreases the time required to respond on a ticket, because the developer in charge of the queue is not loaded with other tasks and focuses solely on making the issue reporters happier. The fact that the process encourages collaboration and forces regular changes of the work mode - between development and support-like activities - contributes even more to the overall efficiency. Finally, when developers face a load of raw external bug reports from time to time, they become more aware of customers' needs and position and thus more motivated to do a better job.

As usual, these benefits don't come at no cost and there are some problems that one may face while establishing Watch in a development team. In particular, developers accept this activity differently and some may really hate it - especially in the beginning - which may require additional management work and certain adjustments. The way people handle the bug reports also may differ. For example, some initially focus on fixing items instead of doing faster pre-processing to provide quick response to the customer and adequate information to the person who can address the issue better. Or it may happen the other way around and you may face a situation when very little analysis is performed before stacking a request into the backlog. What may be most scary, almost inevitably performance of the team will drop after the process is introduced - each week one developer will devotes all of his or her time to an unfamiliar activity instead of actual development, but this is a short term effect and it diminishes quite quickly.

We faced some of these issues when we introduced this process in our team, but despite this the change looks totally positive. The fact that I, as a team leader, got partially freed of this activity and now have more time for other business obviously makes me happy, but its effect on our cumulative efficiency is way more important. In particular, the process boosted developers' expertise and domain knowledge growth significantly. I also saw it work great during my recent vacation without any extra effort on my side - last year it was different. However, what surprised me most about this organizational change is that, while originally I expected a strong pushback it was accepted positively by the teammates. So it is almost a year since we began our Watch and I'm absolutely sure that I would never go back to the old way of handling incoming issues.

воскресенье, 17 июля 2016 г.

Everyone on your team is important

Over the course of my career in software development I got used to the idea that the key thing we deal with everyday is complexity. It shows up on different stages of our jobs, but most interesting cases are of course tricky bugs. Sometimes the issues reported by our QAs and especially by end-users get so messy that it takes a full crew of professionals to fix them. I have faced one such example recently and in addition to providing valuable experience and the feeling of satisfaction in the end, it served as an evidence supporting that all the roles we have in our team are of great importance and play best when assembled into a team.

About a year ago we have fixed a couple minor issues in our system. To be sure that we have that piece of functionality working properly despite any future changes we decided to create automated tests for it. Our QA engineers did get to this task, but because life is what happens when you have other plans, that happened only recently. Roughly at the same time another member of our team has found an issue in a distant part of the system, totally unrelated to that first bug, and fixed it using a certain code construct.

Once these two things met in our repository the new tests failed. It so happened that the developer who was digging into the failure wasn't in any way familiar with any of the two changes. The error conditions turned out to be tricky, so some folks joined investigation and it was eventually traced down to that seemingly unrelated last fix to be routed to the appropriate platform developer and fixed there. The importance of this event however took some more time to be recognized.

Another chapter of this story starts several months earlier. One of our customers filed an unpleasant data bug that we never managed to reproduce and understand. It was hanging there unresolved and made partners, so to say, unhappy. It's high severity kept it visible for us, but we still weren't able to do anything about the issue - till the moment when the events described in previous paragraphs took place. When I was doing a daily review of recent and priority items, I stumbled upon this nasty bug first and then spotted the one that caused all the mess outlined above. Something about them looked similar. The way the latter one was fixed suggested a new idea for repro steps for the former. Late at night this fresh approach was attempted and yielded positive result - we not only understood the reason behind the high-priority issue, but also knew that it is now fixed and could report that to everyone concerned - the problem was with the same code construct and the same platform issue that started the first bug.

This basically means that the data problem that we were so worried about took us a couple QA engineers, two developers, a product manager and a team-lead doing their usual jobs to track it down and fully understand - not to mention the significant amount of time spanned by all the activities that led us to this result. This might indicate problems with the product being developed or with the process, but the truth about software development is that such problems that eat enormous resources do happen and, if you are working on a large and sophisticated piece of code, you can only minimize them - not get rid of them completely. Complex issues also require different people to handle them - sometimes just to throw enough pairs of eyes and points of view at the problem. Even worse, there is an element of a slot machine to this requirement, because you can only partially design how and when someone of your team-mates will play his role in such a process - in most cases these revelations and discoveries happen merely because a number of people are doing their work properly.

Software development is sometimes messy work - especially when we speak of business applications. I would like to say that it doesn't have to be like that, but it seems that for most products getting complex enough to give birth to problems of this kind is just a matter of time. As a result we do need all of our people in their positions - QA engineers, developers, analysts and so one. This incident that we faced highlights the fact that even when I have no clue at all about the job that some people are doing at our office, it is still very valuable. The beauty of a development company and its processes is that one day any one of us - or rather an unexpected combination of our efforts - may save the company from something nasty or bring it to a new solid achievement. This simple idea is also a great motivator, because when I get tired with the routine and think that the work I am doing is useless, I can remember how almost the whole our team won the battle with that bug and see that one day me doing my job may save some thousands bucks for the company and countless hours of work to my colleagues.

вторник, 14 июня 2016 г.

Take-aways from the Getting Things Done book by David Allen

Over the recent years I heard about the Getting Things Done methodology here and there, but only recently I finally read the book by David Allen and got closely acquainted with its idea. It turned out that I already employed some of the techniques that make up this famous self-organization system, but in most cases never acknowledged that there are bits of GTD among my tools. More importantly, the book taught me some new tricks and the proper way to combine and use the tools.

The most well-known bits of GTD are inbox and action lists. The inbox is used to record every bit of information that arrives to you for subsequent sorting. The action lists - to keep track of the actions that you need to take on duty, at home, when there is time to make a call, etc. I was already confidently using these by the time of reading the book, so this part didn't surprise me. The concept of projects, however, was more of a new thing to me. In Allen's terminology a project is anything that you're going to do and that will take more than one step to accomplish. This a bit unusual understanding of the term is combined with the idea that one must maintain a full list of their projects and review it regularly to decide on next actions. This implies that every week you take time and go through the entire list and for each project decide what you will do next to get the thing moving forward - and add these actions to your lists. This (quite obvious, to be honest) way to handle projects is my most valuable take-away from the GTD book, because when I am serious about weekly reviews it results in advancing a lot of things that I would normally forget about. Instead of making me drown in a chaos of numerous small projects, which I originally expected, this approach actually helps me follow all the projects that I deem important and makes sure that each of them is gradually moved to completion.

Another idea that resonated with me greatly was that one should have a reference info storage and have it in perfect order. And the order here is not just a matter of beauty, but a quality that allows one to put something in easily or to get whatever they need at a particular moment quickly. One manifestation of this idea is that reference information gets separated clearly from the next actions information. For me this played a huge role, because I'm the kind of a person who's obsessed with historical data, archives of all sorts and being able to remember what I was doing on a particular day three months ago. While this might be important in some cases, to achieve results one should see the current context and aim for future, using the past only as a reference. This transformed some of my routines a lot and helped to progress more efficiently both at my work and with other parts of my life.

Another goal that one sets when implementing GTD is to build a reliable system of reminders and ensure that one remembers the right things at the right time. That's the famous wait list as well as the usage of the calendar and even the checklists (which I embraced as a great tool before diving into GTD). It turns out, however, that there is only one way to achieve this goal - that is to organize all the information on next actions and events properly and review it regularly. Regular routines seem to be the second pillar of GTD - together with ordering your life. These are applied virtually to everything - from the mission and principles to the projects and next actions lists - and I can confidently say that regular reviews of my current standing are so critical to efficiency, that I could get little use from the GTD system without this bit. On the other hand, having finally developed a habit of doing these reviews, I now see way more control over different parts of my life and a significant increase in ability to control progress towards multiple unrelated goals.

There are many other useful ideas, but David Allen certainly explains them better than I, so if you're interested you should read it. I was quite impressed - in particular, because it was easy to apply some of the suggested approaches and to see how these yield more control over my life. Another thing that makes the book a great read is that it is very practical and goes to the level of an engineering textbook into the details of organizing yourself. At the same time it doesn't offer a magic pill being pretty honest about the amount of time and effort that one has to invest into personal management to implement the suggested approaches. If you're OK with that and are interested in pursuing efficiency, I do recommend to read the book and to make that investment because it will pay off quite quickly. Have a nice reading!

воскресенье, 15 мая 2016 г.

Using an external JavaScript library in your ClojureScript application

My recent playing with ClojureScript and Om was a great trip into the field of functional front-end programming. However it was disrupted by a small yet irritating problem. I wanted to consume an external JavaScript library - the client for Trello API - in my Om application and call a method of the object defined there. It all worked fine while I was developing the thing, but the first attempt to deploy it to the production server resulted in my application not working and spitting errors like "Trello.rf is not a function".

This problem is caused by ClojureScript munging names during compilation, which is disabled - as well as other optimizations - in the default dev profile. Because the idea of using an external JS library is not something completely stupid, there are some ways to avoid munging external names - outlined here and here. The approach with the externs file is quite easy to adopt, but it took me some experimenting to make it work, so I will describe it step-by-step here to have a reference in future.

Inputs first. I have a ClojureScript Om application enabled by cljsbuild 1.1.3. The compiled JavaScript is served from the /resources/public/js folder (or from somewhere under it). My application uses the Trello client JS library to make one call to the Trello.authorize method. The Trello library has to be brought in through the index.html like this:

<script src="https://api.trello.com/1/client.js?key=myappkey"></script>
<script src="js/compiled/trellodonelist.js" type="text/javascript"></script>

To prevent ClojureScript compiler from turning the names defined in the external library into strange symbols we only need to introduce an externs file and supply its name to the compiler. All this is done in three simple steps:

1. Create a plain JavaScript file and store it in a place where the compiler will be able to find it. I chose /resources/public/js/externs.js (the name doesn't matter that much).

2. In the externs.js file touch all the names from external libraries that you plan to refer in your app. For me that looked like:

var Trello = {};
Trello.authorize = function() {};

3. In your html refer the externs file along with the external library and your compiled ClojureScript:

<script src="js/externs.js" type="text/javascript"></script>
<script src="https://api.trello.com/1/client.js?key=myappkey"></script>
<script src="js/compiled/trellodonelist.js" type="text/javascript"></script>

4. In the project.clj file add the path to the externs file in a vector under [:cljsbuild :builds :app :compiler :externs]. You aim for something like this:

(defproject trellodonelist "0.1.0-SNAPSHOT"
  ; various meaningful things
  
  :cljsbuild 
   {:builds
    {:app
      {:source-paths ["src/cljs"]
       :compiler {:main trellodonelist.core
                  :asset-path "js/compiled/out"
                  :output-to "resources/public/js/compiled/trellodonelist.js"
                  :output-dir "resources/public/js/compiled/out"
                  :source-map-timestamp true

                  ; this one
                  :externs ["resources/public/js/externs.js"]}}}}

  ; other meaningful things
)

I had to play a bit with various locations of externs.js and missed one thing or another, but the above setup finally did the trick. Now, even if the ClojureScript is compiled with all the optimizations, the compiler is aware of the names brought in from the external libs and won't change them.

There are some other ways to make external names work and I recommend to read the articles mentioned above to get a better understanding of the reasons behind the issue and a wider set of alternative solutions. For my simple case the self-made externs file seemed the easiest and the most concise approach - maybe it will suit you as well. Happy coding!

воскресенье, 1 мая 2016 г.

Initiative

Some months ago we wanted to get a new developer on the team. My personal desire was to get him as soon as possible and of course I needed our recruiter's help with this. We had a short discussion with the lady about the kind of a person we'd like to hire and I was sure that we got on the same page and seeing the right candidate is only a matter of days. However, some weeks passed and I wasn't getting any resumés and not a single interview was scheduled. I was concerned almost to the point of going to the recruiter with the WTF?! expression in my face. Fortunately, though I chose to spend some 20 minutes writing a short description of two types of candidates that fitted my needs and emailing these to her.

What happened next surprised me a lot. Almost instantly I started getting a constant stream of CVs that matched my descriptions. It took us a couple days to schedule the first interview and only two or three weeks later (which means pretty soon in this context) we made an offer to a bright young fellow.

This story taught me a great lesson. I may believe that I have agreed on the goals and plans with someone, but if there is a slightest chance that doing some simple thing may help them get going I should do it without doubt and waiting. Spoken agreements made with your peers in a walkway are rarely clear and may easily get pushed away by newer and more comprehensible tasks coming from elsewhere. On the other side, taking some initiative to follow up and elaborate the problem may bring tremendous results.

Another example that I have on the same matter is concerned with an internal knowledgebase for developers. Despite having a vast code base rich with patterns and non-trivial solutions to various problems, we had limited guidance on this treasure and the reasoning behind its bits. Certainly our developers could benefit from a knowledgebase that would collect advice on various development questions.

I had a plenty of ideas on why we didn't have the knowledgebase, including the conspiracy theory that my other colleagues knew why such a thing wouldn't work. It turned out, though, that the only real reason for its absence was that being loaded with other tasks we simply didn't chose to set it up at some point. Thus, once a dedicated section was created in our internal wiki and declared a place to store all development-related knowledge, I started seeing different people contributing an article or two or simply voicing  support for the idea.

Starting things is difficult and if that's true for you, it's likely the same way for your friends and colleagues. Sometimes people lack a clear picture of the destination, in other cases they simply don't perceive the goal as important to anyone. No matter what's the core reason, just showing some gentle initiative may be enough to start the fire and get things going. It's only important to remember that initiative is not just about talking or thinking - it's all about acting, making the first step and showing the way.


воскресенье, 13 марта 2016 г.

Checklist as an Individual Efficiency Tool

Checklist is a tool that is widely used to facilitate completion of sophisticated tasks that involve many activities and actors. Software development teams benefit hugely from such things as release checklists (make sure that tests pass, release notes are ready, the build is uploaded somewhere, etc) and feature integration checklists (similarly, old tests pass, new integration tests are automated, documentation for the new functionality is written, the thing is accepted by QA, etc). The tools of this kind serve greatly to coordinate the actions performed by different persons or teams. At the same time, I believe that checklists also bear great power as an individual efficiency tool and that's where I didn't see them used much.

I first employeed a checklist for my own use when I tried to add some structure to my weekly task of analyzing team's performance and planning the next sprint. Even though I had a certain routine and even a quite stable form of the final result of this activity (a report and a list of Jira items for the next week), sometimes I forgot to check certain aspect of our performance and highlight it in the report or overlooked some items that needed action during the next week. Thus my key intent was to provide myself a list of things that I must do to consider both the report and the plan done. Additionally, I wanted to review the process of preparing the plans to exclude any obsolete steps. The results were great: once I put the new checklist into action it started saving me about an hour each week (half of it from mere thinking of "what else I forgot to include in the plan"). Additionally, after I defined this routine explicitly it became easier to think about it and spot other items, which don't bring much value or get ignored consistently - and to exclude or rework them, saving myself even more time and nerves.

There are several similar checklists in my toolbelt that facilitate other tasks and work great, but there is also a bit different thing that I consider my greatest finding - that's my daily checklist. The initial idea was to have a short list of things that I wanted to do daily and to go through it every evening before leaving the office. I quickly got used to the routine of tagging portions of job on the list as done at the end of the day and at some stage expanded it to ensure that I don't forget some other things - the benefits turned out to be tremendous.

First of all, making these routine checks helped me acquire the habits I wanted and somewhat refocus my work on the things I consider important. Additionally, since putting this checklist into action I had much less cases of forgetting to do certain things, because I get reminded of them before I go home. One change that I didn't expect is that this routine made me generally less nervous about the job: I don't have to rush into reviewing every new pull-request or responding an email from partner, because I know I will notice these things later: looking into both pull requests and mail inboxes is on the list, like other similar things. Thus the checklist makes me more efficient through decreasing the stress of urgency, serving as a reliable reminder and paving a short path to making myself more disciplined. In the end all this boils down to freeing my mind for more important tasks than just being afraid to skip something important.

Another powerful thing about this list of daily tasks is that it forces me to check some aspects of the team's current situation at the end of the day, which sometimes produces unexpected results. For example, I had several cases when this review allowed to spot urgent tasks and made me adjust plans. Furthermore, sometimes the nightly check made me notice a vague relation between a problem that we were dealing with during the day and a different problem that sit in our backlog waiting for us to find a way to address it. At least once such a discovery allowed our team to track down and fix a data corruption issue that bothered our partners a lot - and it could have evaded us again if I didn't perform the routine review of our status that evening.

To achieve these benefits one must have an efficient tool to implement the process. Ideally it should allow to manage checklists and to perform daily planning at the same place. Since the most common result of going through a checklist is adding something to the tomorrow to-do list and you want as little a gap between these two actions as possible. I use Trello agile boards for planning and to my great luck they support creating checklists inside cards and copying them freely (which also means that they can be included into the daily plan). If you didn't see the service yet, make sure to check it - that's a very handy tool! (Disclosure: even though I'd readily accept money, Trello doesn't pay me for this little advertisement.)



It's true that using checklists routinely - especially daily - may require some discipline and motivation. Thanks to the amazing 'To the Moon' talk by Russ Olsen, when going through my daily checklist I always feel acting like Neil Armstrong and Buzz Aldrin did before transmitting "Houston, Tranquility Base here, the Eagle has landed". It might seem childish, but it's fun to feel like you've just landed a moonship when you're simply leaving your office at the end of a long day - and that's a part of my motivation.

суббота, 27 февраля 2016 г.

Picking a simpler approach to aggregate data in Clojure

The only purpose that I use Clojure for now is talking to our Jira to extract some statistics for analysis and even this still brings a lot of opportunities for discoveries and revelations. The most recent task that I set for myself was to get data on bugfixing activities over some period of time and store it as a table for further analysis with Excel.

I wanted to transform a list of Jira items into a table that shows how many items of different severity each team member had fixed on a certain day. I already had a way to talk to Jira so the key part of the task was to aggregate the list into a table with 3 attributes and one numeric value - the count of items holding this combination of attributes. Aggregation is easily done with reduce so I only needed to chose the form of the result. My first natural response to a problem of this kind is to assemble a structure of nested dictionaries with values of attributes as keys and summed count of items as leaf values, something like this:

{ "2016-02-10"
    { "Ivan Petrov"  { "Major" 1 "Normal" 2 }
      "John Stone" { "Critical" 1 "Normal" 1 }}}

It turns out that Clojure 1.7.0 offers the update-in function that works greatly with nested maps. The thing takes the hashmap, a sequence of keys and a function. It would first retrieve the value currently stored in the nested map under the specified sequence of keys, apply the function to that value and store the result back under the same keys. Thus transformation of the list of items into an assembly of nested maps holding aggregated values will look like this:

(defn resolved-bugs-stats [issues]
    (reduce
        (fn [report {date :resolutiondate assignee :assignee severity :severity}]
            (update-in report [date assignee severity] (fnil inc 0)))
        {}
        issues))

This piece of code yields the figures that I want - the only step left is to transform it into a sequence of rows and this one took me much more thinking. Despite the fact that I was able to find a solution, I also realized that I don't need the nested structure at all. (That is availability of update-in turned out to be a misfortune).

The essence of my revelation was very simple: why would I build a map of maps of maps if I need a list in the end? Since Clojure lives great with vectors as keys I could just use a composite key and go with a one-level hashmap. This single level is still required if I want to have an easy way to sum up the count of items with certain values of attributes, but unlike a nested map it transforms very easy into a simple table. Here is the new function - it looks almost the same but produces a simpler result and, what's more important, makes the code that uses it way cleaner:

(defn resolved-bugs-stats [issues]
    (reduce
        (fn [report {date :resolutiondate assignee :assignee severity :severity}]
            (update report [date assignee severity] (fnil inc 0)))
        {}
        issues))

; generates a result in the form:
{
    ["2016-02-10" "Ivan Petrov"  "Major"]   1
    ["2016-02-10" "Ivan Petrov"  "Normal"]  2
    ["2016-02-10" "John Stone" "Critical"]  1
    ["2016-02-10" "John Stone" "Normal"]    1 }

While modern programming languages offer powerful tools to make complicated solutions real and cheap, there are few cases where the form of intermediate data structures need to be significantly more complex than the form of the desired result. Simply keeping this idea in mind and evaluating our solutions against it may help avoid some of the excessive complexity that we introduce when building systems.