Saturday, July 25, 2015

Being a Code Custodian



The roles of a team lead can vary dramatically depending on the team dynamics. Being a code custodian may or may not be appropriate for your organization. I have found it valuable in some situations. There are automated and manual approaches to monitoring the integrity of software. I like to apply both when appropriate. Each has pros and cons:

  • Automated Approach - Usage of software tools to detect potential defects and/or style violations
    • Advantages
      • Can check for a large number of potential defects and/or design smells
      • No ongoing time investment after initial investment to setup and configure
      • Can be tied into CI environments like Jenkins to guarantee adherance
    • Disadvantages
      • Not good at catching the most important issues
      • If rules are configured too restrictively, becomes more of a hindrance than a benefit
  • Manual Approach - Human review of checkins or some subset of checkins
    • Advantages
      • Only way to catch many types of issues
      • Keeps the reviewer abreast of new development
      • Leads to mentoring opportunities for the reviewer
      • Good way for the reviewer to learn from other approaches and pickup new techniques
    • Disadvantages
      • Difficult and important to be completely objective and remove opinion from feedback
      • If applied heavy-handedly 
        • Kills developer empowerment
        • Can be disastrous to morale
      • Time-intensive
      • Spotty
        • Usually unrealistic to fully check everything
I would further decompose automated approaches into 2 categories.
  • Category 1: Surface Analysis - Static-analysis tools including lint, checkstyle, and findbugs
    • Advantages
      • Customizable
      • Rules-based
      • File-driven
      • Easily integrated into many modern IDE's
      • Easily integrated into build or CI environments via a CLI
      • Many free or open-source
      • Often mature and robust
    • Disadvantage
      • While they can catch many "surface" issues, they don't always dig very deeply
  • Category 2: Deep Analysis - Tools that looks more deeply into aspects such as cyclomatic complexity, memory usage, and performance. 
    • Advantage
      • Tend to be higher value 
    • Disadvantages
      • Difficult to automate
        • Some intended more as an aid for manual test and debug
        • Some produce results that should be inspected and interpreted
      • Can be difficult or impossible to quantify rules for
      • Less typically free or open-source

In the next two posts I will delve into how I applied these tools in recent projects.

Saturday, July 18, 2015

Design and Architecture in an Agile Environment




I have worked in several "scrum-like" agile environments over the years. I say scrum-like because each adopted some of the principles, but was unable to embrace all. In almost every case the biggest casualty tended to be an inability to totally isolate the development team from shifting priorities in the middle of a scrum. This is a difficult problem to address, particularly when working with exceptionally demanding customers, and perhaps a topic for another day. Another possible discussion item is whether or not "partial-scrum" is scrum at all.

One of the industry trends with agile environments is to tradeoff up-front design and architecture for an "emergent design" iterative environment where coding starts immediately. However, I would argue that agile does not, and should not, imply that there is no up-front design. Iterative development does not mean that you should rewrite the entire system in every iteration. Nor could anyone reasonably argue that this is an efficient use of the team's time. Yet, if you jump into implementation without any design the chances of "coding into a corner" greatly increases, potentially producing exactly this type of situation.

So we shouldn't give up design and up-front thought any more than we should give up good coding standards. Fast development does not have to equal reckless development. A technique I have used in some organizations is to have one or two senior developers/architects sign up for design and architectural tasks as deliverables for the sprint. These deliverables can be tracked much like any other deliverable, and are as integral to the sprint's success as code and QA deliverables. The end-result should be a light design in some electronic format, with buyin from the team and stakeholders.

The only real disconnect with the scrum process of adopting this technique is that typically the one-or-two people working on this task have a different focus from the rest of the team. This work is synergistic, however, as it results in a better definition of development backlog tasks for subsequent sprints. From this perspective, the team performing these tasks could be their own scrum, but often this is not practical because of how small the team is (could be one member) and because the members will often also take other tasks from the ongoing development sprint.

Another approach that I have seen work is to break up chains of sprints with planned one-or-two week intervals for the team to do design and architecture of  backlog items and cleanup work from prior sprints as needed. Whether this work is considered a sprint or not is up to the team, but I have found that occasional "unstructured" time is also useful for a team.

"Does design and architecture play a role in scrum? If so, what techniques have you seen work and which have you seen not work?"

Friday, July 3, 2015

Balancing Performance with Code Readability : Case Study (Android, Java)




Image result for code complexity
Google has a number of blog posts on how to write code for Android performance. They make recommendations that conflict with normal Java standards such as avoiding enumerated types and using counter-type  loops over their collection types. They make good arguments for comparative performance improvements for each, but I think it's good to step back and evaluate Google's motivations before blindly applying all their recommendations.


  • Motivation 1: Bad apps make Android look bad

    From Google's perspective, a few misbehaving apps can make the entire ecosystem look bad when compared to the highly controlled ecosystem of Apple. This is especially true for apps that are bad citizens (e.g.- consume more resources than needed for longer than needed, services that run longer than needed, activation of power-hungry resources like GPS hardware for longer than needed). Other recommendations are performance-oriented. There is little motivation for them to recommend well-written code over code that grabs every nanosecond of performance. They aren't the ones supporting the apps, after all.


  • Motivation 2: Wide range of hardware capabilities

    Often times developers will test only on a high-speed device over WiFi or 4G and not understand the implications of how their app will run on older or less expensive hardware or over slow Edge networks in China or elsewhere. With the advent of smart devices like watches, the limitations can be even more severe, especially for  battery life.

While I strongly endorse writing programs that are good citizens, other recommendations to grab every nanosecond of performance I often find as not always necessary. I also have the luxury of architecting, designing, and developing apps for specific high-end Android devices, which makes motivation 2 less relevant. Even if this were not the case, I still prefer to write first for readability and, only when proven necessary, refactor for performance.

An example of where I recently found this  useful was in creating code whose function is coordinating an external set of contacts with the Android native contact database. There were a number of fields that were to be synchronized and the list of exactly which would be synchronized was subject to change. I found code in another application that someone wrote to do something similar as a reference. The author of this code created a string projection  where each ordinal was referenced by number into that projection. Insertions or deletions of values in this projection would change the meaning of each ordinal, making the code very brittle.

I introduced an enumerated type for storing the elements of the Android database string projection and relied on the ordinal position for later retrieving columns from this projection. The structure was thus "self-aligned" making for easy removal and addition of elements to the projection. The enum looked something like this:


Access to the columns, could then use the ordinal of the enumerated values like so:
instead of using "magic numbers" which rely on the order of the elements in the projection (in this case it would have been "1").

The new self-aligned approach is both much more readable and less susceptible to bugs as the code changes and there was no noticeable performance hit.


Saturday, June 27, 2015

Balancing Performance with Code Readability

Image result for balance

This is a topic much discussed in the industry and the position I take is a common one: Code first for readability and support, refactor for performance as necessary. There are notable exceptions to the wisdom of taking a readability-first approach. In general you will know if your code is one of these exceptions. It could be graphics-intensive operations, operations widely used and distributed in libraries, and similar situations where you know critical paths through the code a priori. For the 90% case, where squeaking out every nanosecond of performance is not critical I like to use the following steps:
  1. Write code to be as readable and supportable as possible
  2. Validate that the performance is acceptable
  3. If performance is not acceptable, profile to find the hot spots
  4. Identify techniques for improving performance of these hot spots
  5. Using the Strategy Pattern
    1. Extract each module to  be refactored into a separate component and introduce a simple factory to serve up the existing implementation
    2. Introduce a higher performing implementation and update the factory to use this new strategy
    3. Add comments as necessary to the new strategy implementation
The factory gives us much more flexibility. It allows us to restore the original strategy if we later find that we really don't gain enough overall performance to warrant the new version. It also allows us to experiment with different strategies for improving the performance before deciding on one.

I am of the school of thought that a lot of comments in code is a design smell that the code wasn't as well thought out and decomposed as it should be. I think that most code should be written to be self-documenting with appropriately named methods with small scope. There are certain exceptions to this. Writing for performance is a common way of adding unintentional complexity. Often this added complexity is unavoidable. We should add sufficient comments to explain the code and the reasoning behind the extra complexity so that someone further down the road does not reverse our decisions (perhaps for readability) without understanding the implications of that reversal.

Next week I will push into a use-case where I opted for readability over performance in conflict with published performance recommendations from the platform vendor.

Saturday, June 20, 2015

Effective Code Reviews: Case Study


As mentioned in previous posts, mentoring is an important and rewarding aspect to software leadership. One of the more effective ways to teach is via thought-out code review feedback. Performing code reviews is also an excellent way for the reviewer to learn and pick up new tricks. I often find it helpful to be on each end of the code review.

I solicit code reviews of my work when coding something particularly mission-critical and/or complex and encourage the same from other members of the team. There are many ways to run code reviews and perhaps I will discuss this in a future topic. Code reviews can involve a team or be one-on-one. The case study discussed here was a one-on-one case where I was the reviewer. Usually this has worked well for me. This situation had some unique challenges, however. Specifics of this situation:
  • Working with a senior, talented, open-minded developer
  • Developer is a non-native English speaker
  • Developer is very open to feedback
  • Developer's style resulted in overly complex code
  • Developer frequently requested reviews
  • Most of the request reviews span numerous modules
Problems this posed for me:
  • Often times the code was just too complex for me to perform an effective review and root out potential side effects
  • The language barrier reflected itself in the code in the naming of methods and variables
  • The time demands of the frequent requests were too high

To address the time constraints, I applied a common technique, which was to ask to limit the scope to areas of highest concern. This did not work, as the developer was not able to identify specific areas. The inability to identify "chunks" that could be easily identified to treat in isolation was another symptom of the complexity.

This situation continued over a period of weeks. The bulk of my feedback centered on addressing the complexity issues and was in the form of email. However, each time I did this I felt I did not grasp the nuances of the changes well enough to provide adequate feedback. I found that the developer would make the specific requested changes and then the next day make the same complexity mistakes in the next piece of code that he submitted. To make matters worse, we were in a critical part of the project where time pressures were significant.

The real fix is to fully refactor the functionality. This is something we scheduled, however, it was not realistic in the short-term, so it was important to stabilize the code as much as possible. Equally important was my desire to help this talented developer with his complexity issues so that the problems would not be repeated in future projects.

Finally I decided that, despite the time-zone challenges and the additional time demands, the most effective way to perform reviews in this situation was to do them interactively with the developer. While voice is normally my first choice, in this situation I found that interactive IM sessions were more effective. There are two reasons for this:

  1. It gave both parties an opportunity to think carefully before each reply
    • From the developer's side this extra time gave him the opportunity to translate as necessary. 
    • From my side it gave me time to analyze and provide recommendations as we went through the code.
  2. It provided a written transcription for reference during and after the discussion. 
My biggest hurdles were understanding the motivations and side effects of each of the changes. So the review then became something like this:

  • Me: Method X, lines Y-Z: What are you doing here and why?
  • Dev: Explanation
  • Me: Did you consider factors A and B?
  • Dev: Actually, I think there may be a bug C 
( One of the things I like best about code reviews, whether I'm reviewing or being reviewed is that in discussing the code often the coder himself will stumble on issues)
  • Me: What if you took this 4-line return statement and simplified it to a method call
etc.

The end result was that I felt I was finally able to provide effective feedback and identify specifics of how to reduce the complexity of the code.

The lesson learned here for me is to not wait so long to consider alternate communication mechanisms if I find one mechanism is not working well. This is a lesson I try to apply to many other aspects of communications.

Friday, June 12, 2015

Key traits of a software leader - Dedication, Love for Mentoring, Balance and Perspective



  • Dedication

Leaders and senior developers often know what to do, without the need for external direction. Of course you must do what your manager asks of  you, but once you start on it, you should usually be able to complete the task without requiring management oversight. Are the requirements clear and complete? If not, find out who to talk to get them clear and complete. If you can't get clear requirements from the prodouct owner for this task, come up with your own clear and complete requirements (written or in the form of tests) and then give these to the product manager or manager as a starting point. Hold a meeting with the stakeholders and leave with the requirements clarified. Take a similar approach for every other aspect of the delivery. Push for answers you need, make proposals along the way, always get buyin from others of the team, and don't be afraid to hold meetings when email, issue tracking systems, or other communication mechanisms fail you. Ensure that requirements end up in a written form that is signed off on by all stakeholders.
Whether it's the near-term objectives of a sprint or longer-term objectives, a leader is strongly invested both in his/her own commitments and in the teams' commitments. Are others on the team in need of your help? How can you ensure the success of the entire team? Like everyone on the team, leaders will put in extra hours during legitimate "crunch" times to meet important milestones. This does not mean that you should be expected to work 80 hour weeks ad infinitum. Learn when it is truly important to put in the extra hours and when it is unreasonable. If you are like many developers, you enjoy your work enough to want to put in longer hours. Instead of putting in an 80 hour week for your current sprint, consider spending time learning on your own and/or playing around with new technologies if not in a crunch period. This is also important to the team, as you can bring what you learn to the table to help in design of future corporate initiatives.


  • Love for Mentoring

One of the most rewarding aspects of working on a development team can be the opportunity to help with the growth and learning of others on your team and teams you work with. There are many aspects to mentoring. Consistent application of the traits described in this blog series on leadership alone is a good way. From an early age on, people emulate the behavior of people they respect. Find other ways to personally connect with individuals on the team. Choose one or two on the team who are most engaged with you and work closely together. Often this will be more junior people on the team, often you will select people who show the most promise, but neither of these is a requirement. Remember that anyone with more experience in any one area can act as a mentor for anyone else in that area. You could easily end up helping someone older or with more overall experience, but less in one or more specific areas. Lastly, there are always opportunities to find others to help mentor you. Nobody is ever done learning and there are always people to learn from, whether on your team or externally. Try to spend as much time finding people whom you respect in an area you would like to grow in as you do in trying to find others who can learn from you.


  • Balance and Perspective - Understanding of when and how much technical debt to accept

In constant conflict with your goal of delivering personal excellence will be the organization's need to deliver software in a timely fashion. A true leader will understand enough of the market dynamics to know when it is appropriate to "rush a solution" and when the constraints are artificial.
Organizations tend to constantly apply time pressure. You should understand when there is a true market force for a rush and when it is self-imposed by the organization. A clear-cut example of a true market need is delivery of software to a hardware product timeline owned by another organization or company. If there is a true market need, often you will be forced to create some "technical debt" and deliver software that is not as well written as you would like. In these situations, a leader will still strive for quality and test coverage in the solution, and sacrifice architectural and/or design integrity. In other situations, the time pressure may be internally-driven. You should act as the code-base custodian. Make sure that management understands the trade-offs of technical debt for this decision and weighs its long-term supportabilty costs. Importantly, a code-base custodian looks for appropriate times when external pressures are not as high to lobby for an investment in paying down technical debt from previous "rush jobs".

Saturday, June 6, 2015

Key traits of a software leader - Personal Excellence, Integrity, Innovation, and Technology Evangelism



  •  Personal Excellence

Software is continuously evolving and so must software professionals evolve. Find ways to continue to grow. There are numerous ways, including podcasts, books, blogs, meetups, technical conferences, code camps, and innovation challenges. Find a couple that fit your interest and style. Use resources like github and stack overflow to learn and experience other approaches to writing code and solving problems.

Leaders develop well-thought-out, well-architected, and well-tested software. Make everything you write something that you would feel comfortable publishing to open-source. and others on the team will emulate your style, template from your code, and learn from you.


  • Integrity

Integrity is an important quality often overlooked in soft-skills discussions. Yet, integrity is arguably one of the most important aspects of leadership in any field. Never do anything unprofessional at work our outside of work with any of your colleagues. Treat all members of the team with respect and consideration...always. If you disagree with someone you should always make your voice heard, but your voice should be the voice of logic and reason, not the voice of emotion and ego. You don't need to be loved by everyone on the team but it helps to be well-liked, and you need to be highly respected.

Apply this integrity to your work ethic. You should provide aggressive schedule estimates that you can achieve with high confidence. You shouldn't have hidden agendas. Make choices and recommendations that are best for the team and organization. If this doesn't align with your goals, choose another team or organization that does.

  •  Innovation

Innovation is perhaps one of the most difficult traits to learn. For an existing problem, it is easy to get caught up in what is familiar and to fit problems to existing toolsets and skills that you have always developed. Try to push beyond what is comfortable and known. Allocate some time before jumping into any task to thinking about what might be the best solution for your problem. Do this before providing schedule estimates.

Innovation also extends well beyond solving existing problems. Look around at the expertise of your group and your company. Periodically step back and think about other things that your group or company could invest in that would be a good fit for the company. Discuss some of these ideas with your manager or product manager and track them somewhere appropriate at your company, such as a wiki. If you strongly believe in your idea, don't be afraid to champion it or even spend some of your own time to develop a prototype.


  •  Technology Evangelism

A strong leader will keep abreast of software technologies and advocate adoption within the team where appropriate. This should not become an exercise in constantly adopting the newest "hot" technology. Look for technologies which will improve the development cycle of your team. Would TDD improve the readability and maintainability of your team's code? Do you  have a complex system with frequent threading issues? Would a functional programming language improve the quality of your system? What about your revision control, bug tracking, and/or life-cycle tracking systems? Could your team benefit from improvements to these areas? Consider cost. Some of the best solutions are free. Remember that everything your team does is to improve the bottom line of the company. Often this is indirect and hard to quantify. Examples are investments in test, design, and architecture, which rarely benefit time-to-market, but often have a solid payoff in maintenance costs and ability to evolve the software over the lifetime of the product. Where possible, frame your recommendations in terms of cost savings.