CodeBetter.Com
CodeBetter.Com
RSS 2.0 via Feedburner
           Do you Twitter? Follow us @CodeBetter

Jeremy D. Miller -- The Shade Tree Developer

Under the hood and working with .Net, TDD, Software Design, and Agile Stuff

January 2007 - Posts

  • The Programming Divide

    There's a flurry of great posts and comment discussions going on right now about the divide between good developers and bad developers

     

    The Great Divide

    Like Jeff Atwood, I do believe there is an almost binary switch between effective and ineffective developers.  I'm not exactly sure why this is, but if you'll indulge me, I've got a couple pet theories:

    • People that are good coders are able to do make code work faster, which leads to getting more experience than their slower colleagues, and spare more thought cycles for how to do things better next time.  The innately talented developer is able to get better and faster each time, which gives him or her more time to invest in getting better.  From there I think it's simply a snowball effect.
    • To Phil Haack's point that it's not entirely innate ability, any degree of contemplative thoughtfulness on the part of a developer will make that developer much better over time.  The smartest guy in the world can still be intellectually lazy (but I don't see how you *could* be smart without intellectual inquisitively).  Thoughtful people gain more valuable experience.  People who aren't thoughtful may only be practicing their typing skills.
    • Joannes added this as a prerequisite for being a great developer:  "… a passionate curiosity for software related matter …."  Every very good or great developer I've ever worked with enjoyed software development.  I've been interviewing developer candidates lately, and a passion and intellectual curiosity about software development counts far more with me than Trivial Pursuit Q&A exchanges.
    • Software development takes a very strong ability for abstract thought.  I don't think that concrete thinkers have an easy time with software that's by definition a mental model of something else.  I'd say visualization is important, but I've worked with good developers who had zero visualization ability.  All the great developers I've worked with did though.  People do think differently and have different modes of learning.  

     

    You can get Better, and it's not all Coding or Ability

    Like everyone else I believe that your best bet is to hire the best and brightest, but in reality you have who you have.  Maybe the hiring process gets you the wide swings in productivity, but wringing out a 10-30% productivity gain out of journeyman developers is still a substantial gain.  Training, coaching, discipline, better practices, communication techniques -- they can all contribute to productivity.  "It's not the methodology, it's the man" is a hackneyed and cheap way to get some sort of imaginary high ground in any discussion, but it's partially BS.  Anybody can get better, even if it's only in the way they interract with other people in the team.  Besides, it's extremely unlikely that you can instantly go and replace poorly performing team members with better developers at will.  You're largely stuck with the folks that you have.  Making them better is probably your only option.

    Following a tangent, when any debate about software practices or processes is going on some clown always pulls out this phrase:  "you should just pick the best practices from waterfall, XP, Agile, RUP, whatever..."  Hah!  I've now seized the high ground you might think.  No you haven't, you've simply indulged in the intellectual equivalent of empty calories.  It's a worthless statement.  The question "what *is* the best way to work" is still on the table.  If we knew what the best of everything was, without a shred of doubt, we wouldn't be having the debate, now would we?  Plus it's even harder than that because mix and match practices might not work well because many practices reinforce each other.  I think you could happily add TDD and CI to any methodology and get some benefits, but they shine a bit more in an adaptive process.  On the other hand, doing continuous design or adaptive project management on a codebase without automated tests and CI sounds dangerous to me.

  • Once Upon a Team

    In a post last week I talked about the Anti Team.  It was sarcastic, snarky, negative, and generally kinda fun to write.  I didn't want to leave it like that though, so I want to talk about my ideal software development team.  If you read both posts you'll probably notice that the bad extreme is largely drawn from traditional waterfall shops and my ideal is borderline XP propaganda.  There's a simple cause for that:  I've simply had far more positive experiences working in Agile teams and that's the atmosphere and culture that I think gives me the best chance for success.  Anyway, here's my attempt at being positive and describing the qualities and structure of my ideal team.

     

    See the Whole

    The first ideal is that the team is all working toward a common goal -- making the customer happy by shipping working software.  One of the powerful ideas from Lean Development is the "Fallacy of Local Optimization."  Instead of making decisions to optimize the individual workflows of requirements, coding, and testing in isolation, look at the larger goal of shipping software and work towards that goal instead.  Let's do a deeper dive into the various workflows and see how a local optimization of each could hurt a project overall:

    • If the business analysts define their job solely as creating requirements specification documents, they will certainly act to get the requirements sewn up and signed off as quickly as possible.  I would prefer that the analyst role be defined as determining and communicating the requirements to the rest of the team.  That's a subtle shift in goal, but it might cause the analysts to drastically change the manner and mechanisms for interacting with developers, testers, and customers.
    • Coding is the bottleneck in throughput right?  Not exactly.  Code cannot be shipped until it is tested and deployable.  As anybody who reads my blog regularly knows, I'm a big believer is comprehensive test automation.  That kind of test automation can only happen through very close collaboration with the testers.  I think that testability is a first class design consideration because it directly impacts the throughput of the entire team.  By the same token, testers cannot work if they can't get the latest version of the code onto their desktops.  Ease and reliability of installation is a major contributor to project success, but it only happens with the developers making it so.  Making testable code and easily deployable code might burn more developer hours, but it will get the code out faster.
    • Testing isn't about finding defects, it's about removing the defects from the code.  A tester that works hand in hand with developers to explain reproduction steps and expected results does more good than a tester who simply files defect reports in whatever awful bug tracking tool you use.

    I've worked for over ten years (how did that happen?) on team oriented projects.  One of the first, and most important, lessons I had drilled into me was that it was important to understand the other disciplines on the project.  We spend a lot of time interacting with people that do different jobs than us, and our software doesn't make it out the door without contributions from these folks.  The more you understand about their jobs, the better you can interact with them and support their activities.  As a developer, I can contribute more to the project if I understand and accommodate the needs of my project manager, analysts, and testers.

     

    Know the Why

    I'm going to adamantly stand by this statement:  creating a good design and architecture is easier than socializing that same design throughout the team.  As I've said before, the more specific your instructions are to another developer, the worse the results are going to be.  You'll get far better results if the team as a whole understands the reasoning behind design decisions.  Case in point, I'm doing a WinForms project and that almost automatically means splitting the screens into Passive View's and Supervising Controllers (that's a mouthful, thanks Martin).  Simply telling the other developers that were new to these patterns to avoid putting logic into the UserControl's didn't take because it seemed arbitrary and senseless.  Take the team aside and explain the rationale for the design decision (better testability, splitting behavior from display), and they will be far more able to follow the design because they can make qualitative judgements themselves as they work.  As a technical lead you know you're doing well when everybody, right down to the junior most person, is contributing to your design with suggestions to improve the design.

    Requirements handoff can be a major source of risk and churn.  Even assuming that the requirements are perfect upfront (puh-leeze), the developers' and testers' understanding of those requirements may not be.  No matter how hard the analysts or customers try, the upfront requirements will not cover all possibilities and angles.  At some point the developers are going to have to exercise their own judgement about what the clients want.  Even better yet, developers should be thinking of ways to improve the user experience and contribute back.  That understanding and ability to act happens best as a result of understanding the rationale for requirements decisions. 

    Maybe all "Know the Why" is about is just keeping your eyeballs on the end goal instead of being buried by day to day minutiae.

     

    Maximize Communication Bandwidth

    I believe that the best way to See the Whole and Know the Why is to maximize the flow of communication within the team.  Check out this graphic from Alistair Cockburn's seminal paper Characterizing people as non-linear, first-order components in software development:

    The most effective form of communication is two (or more) people talking at the whiteboard.  The least effective form is documentation.  Why?  Let's think about what's different in the two extremes.  When I'm discussing a design problem with a colleague at the whiteboard we're able to interact, ask each other questions, convey information through body language, and illustrate our ideas on the whiteboard at will.  Exchanging documentation is static and one way.  A great deal of contextual information is potentially lost as the document goes from the author to the people downstream.  There's no room for interaction at all.

    Given a choice, I want the entire team collocated in a single space.  Offices, cubicles, and generally isolation choke off communication and collaboration.  Collocation offers vastly more opportunities for the more effective forms of communication.  I've never particularly found the extra noise of an open office space to be a problem, but maybe I'm just used to it.

    The key point is that I want to talk to all the members of my team. 

     

    Small and Self Contained

    I want my team to be as close to self-sufficient as possible.  Call me paranoid, but I dread any dependency on any person that does not have a direct stake in my project's success.  It takes a significant amount of overhead to coordinate a project when you have to depend on people outside your project that are getting tugged and pulled in many different directions.  You potentially lose a lot of the advantages of adaptive planning and iterative development if you have to coordinate with outside teams whose schedules are not flexible.  I've always thought projects went smoother when I could make adjustments to our database or other infrastructure instead of having to wait for an external group to do it for us -- usually after some sort of artificial paper process.

    Last summer I got to see Bruce Tate give a very good talk about building good teams.  One of his key points that resonated with me was controlling the size of your team to an ideal of 4-6 developers.  A lot of the overhead of doing a software project is creating and nurturing a common understanding and a shared vision throughout the team.  You simply have less communication latency on a smaller team.  Now, to get there with a smaller team of developers, those developers must be able to perform what might have been multiple roles in a traditional project structure.  Personally, I think that makes for a much stronger team.  It definitely makes scheduling much more flexible as one iteration may be 80% database work and the next iteration is all user interface enhancement.  Give me utility infielders over left handed middle relievers any day. 

    And another thing, it's vastly better to have a small team of people who are dedicated (100% allocated) to the project than a dozen people who are working on 3-4 different projects simultaneously.  I can't imagine any thoughtful person arguing with me about that statement, but splitting people across projects seems to be fairly common (in a past life, I was once the responsible architect for 7 projects, at least on paper).  Whatever organizational flexibility you think you might be buying by splitting people across projects is more than lost by inefficient context switching.  Put another way, when I have requirements questions, I want to go ask a business analyst that's 5-10 yards away and completely immersed in the project.  Contrast that to the waterfall experience of tracking down the BA on the other side of the building that wrote up your requirements 3 months ago and has no recollection of why the requirement reads that way.

     

    The Project Manager as Fullback

    For you unenlightened folks who don't follow American football, a fullback tries to blast through the holes ahead of the ballcarrier and pick off any would be tacklers so the halfback (us developers of course) can scoot through on his way to glory.  In an iterative process, you want the developers and testers to be like a machine that runs continuously at a steady hum.  At the start of every iteration a set of detailed requirements is ready to feed into the machine.  I want a project manager who deals with external forces and processes as they come up to avoid stopping the machine.  The best way to keep the machine running is to have Joel Spolsky's Developer Abstraction Layer, and a major part of that is a Project Manager acting as an offensive line looking ahead of the development to proactively deal with any issue that could slow down the developer/tester machine.

    I worked with a project manager years ago that didn't know squat about our process or Microsoft Project.  Quite frankly, the customer and I drove the project as a skunkworks project and she was more than a bit flustered by that.  She might have thrown up her hands trying to rein in a headstrong young technical lead charging ahead, but the one thing she did was get out in front and take care of all the external process stuff so everybody else could concentrate on making the system be good.  To this day, that project is by far and away the most satisfying and successful project I've ever been a part of.  It's only recently that I've come to realize the contributions the project manager made -- even though most other project manager's at that company would have scoffed at the role she played.  

     

    Trust, Transparency, and Reflection

    I owe my teammates a degree of honesty about the progress I'm making and the roadblocks I'm facing.  I need the same from the other developers.  If a project manager is going to be my lead blocker, I need to be very open and tell him or her the problems that we're having or expecting.  If things aren't going well, or taking longer than expected, the PM needs to know sooner rather than later to start making contingency plans.  In turn, I need to be able to trust my PM to admit problems. 

    Needless to say, a blame culture is self-defeating.  The only way to get better as a team or organization is to openly face your problems and find better solutions.  If you're afraid of reprisals from above, those problems are going to be swept under the carpet and fester.

    Of course, eliminating a blame culture isn't enough.  As a team you must be willing and able to be reflective.  Pay attention to what works and do more of it.  Talk through what isn't working, and find and apply better ways.  If there's one single thing I've contributed to my current client's practice, I hope it's conducting retrospectives on a regular basis.

     

    Own the Process 

    In the Anti Team, I blasted centralized process and methodology groups.  The best way for a development team to optimize is to be empowered to own their own practices.  The process should work for the team, not the other way around.  Since each project is different, I think every team should have some leeway to organize their own practices and do so responsibly.  I despise the interpretations of CMMI that require bureaucratic rigamarole before you can make any deviations from the proscribed process. 

    I worked on a nearly fullblown Extreme Programming project with an elite group of consultants that I think would have failed completely -- except for a small change in the way we did iteration planning.  That was the first time I realized that process was a tool that could be consciously wielded instead of some sort of arbitrary set of nonsensical rules.

     

    Work Together

    Here is a quote from a post by Rocky Lhotka that I think is potentially short sighted (emphasis mine):

    Agile is just Waterfall in random order, and with a few different techniques and tools. In the end it all comes down to gathering requirements, analyzing them, translating that into a design, doing some coding and some testing and away you go. You can rearrange the pieces on the board, but they are the same pieces.

    Forget for now any comparison between the polar extremes of Agile versus Waterfall development, it most certainly does matter when and what order these activities occur.  Think about the game you play in elementary school where you line up and you try to relay a message down the line by whispering in the next person's ear.  Inevitably the original message is distorted by the time it gets to the last person in the line.  That's a perfect analogy for sequential development.  What I would advocate is simply hearing the message at exactly the same time.  I think there's an efficiency gain by simply blurring the traditional waterfall phases.  Do all of the workflows for the active features/stories at nearly the same time to maximize the collaboration between all of the disciplines on the project.

    A huge, but common, source of project churn is the testers coming to a different interpretation of a requirement than the developers.  Divergent interpretation always leads to trouble.  By simply having testers and developers start a feature at roughly the same time, and shortly after the analysts for that matter, you can come away with a shared understanding upfront.  Even better yet, if you can get the testers just a bit ahead of coding, the developers can code to unambiguous acceptance tests and know when their code meets the requirements. 

    Human beings don't have perfect recall.  It's easier for me to fix bugs in code I wrote this week as opposed to code I wrote 2 months ago, and it's definitely going to be easier for an analyst to talk over the details of a user story that is fresh in their memory.

    Eric Evans introduced us to the idea of a Ubiquitous Language for all team members.  Roughly defined, the team can communicate quicker with greater ease and less misunderstanding if the entire team is using a common vocabulary to describe the concepts in the system domain.  The lack of a shared ubiquitous language hampers a team when the analysts and developers struggle to understand one another because they're using different terms for the same concepts (or the same terms for different concepts).  Unsurprisingly, I think the best way to found a ubiquitous language is close collaboration between analysts and the rest of team starting very early on and continuing to the end of the project.

    Back to "Know the Why."  The best way to understand the requirements or proposed design is to participate in their formulation.  It's extremely valuable to have the developers and testers involved during requirements and analysis.  The developers can be much more effective if they were there for the early design discussions.  Specifications are just too "loss-ey" to act as a medium for communication.

     

    Constant Conversation

    Thinking and learning on a project never stops, even if it's only directed at getting better for the next project or release.  Good teams challenge their approach every single day and keep up a constant dialogue.  That constant dialogue drives improvement and the shared understanding of the project.

     

    And they lived happily ever after...

    It's late, and I've got to get in early to kick off a 90 minute build before the rest of the team comes in.  Good night all you princes of software development, you kings of code slinging.

     

    I'd completely (and blessedly) forgotten about this one.  Here's another great example of an Anti Team

  • If it changes together, it gets built together

    The title pretty well says it all.  If changes in two or more components or subsystems can affect or break the other, you better get yourself a comprehensive automated build of some kind that exercises the integration of the two.  In particular, and my hot button issue for the day, make database changes and code changes go through the same build and configuration management.  Like it or not, a database and its application are most likely going to be strongly coupled.  If you're writing new code that requires database changes, you really, really need both sets of changes to hit the code trunk at the same time.

     

    Thank you for listening, I feel better now.

    - Roy Moore 

  • The Most Pathetic Scam Email of all Time

    I occasionally get SPAM emails leaking through from this scam:

    During our investigation and auditing in this Bank, my department came 
    across a very huge sum of money belonging to one of our deceased
    customer
    who died on July 30th 2002 of a ghastly motor accident and the fund has
    been
    dormant in his account with this bank without any claim of the fund in
    our
    Custody either from his family or relation before our discovery to this
    development.

    It's a game in the office now to guess which obscure country has the fictional money (I guessed Surinam, my colleague guessed Tanzania, we were both wrong). 

    I noticed at the bottom of today's scam email the idiot's signature:

    Découvrez le Blog heroic Fantaisy d'Eragon! 
    http://eragon-heroic-fantasy.spaces.live.com/

     

  • My DevTeach Talks

    The sessions are all online now for DevTeach 2007 in Montreal this spring.  I'm giving talks on:

    1. The Laws of Agile Design -- How to design and write code that makes the "Embrace Change!" slogan a realistic option
    2. Executable Specifications with FIT -- Requirements, documentation, testing, oh my!  Do all three at one time with requirements that bite back when they're not fulfilled
    3. Put that Wizard Down! -- Design Patterns for Maintainable WinForms Development

    The last one is going to hit the Humble Dialog Box for sure, but we'll go much farther than that into code centric solutions for composite applications, menus, event synchronization, and interactions with the backend.  I think there are many times when just a bit of explicitly written code beats all the drag and drop wizardry in the world.

    I think it's going to be a tremendous lineup and I'm excited just to be a part of it.  The speaker list looks remarkably like my blogroll, and I'm curious to observe the small, but feisty Bellware beast in his native habitat.  

    I figure that I'm good for all the speaking I'll ever do because I've already done the "take the wireless microphone into the restroom" trick and the inappropriate interface name.  It's all downhill from there.

  • Using FIT Testing

    My former colleague Jim Matthews presented and wrote an experience paper for using FIT acceptance testing on development projects that describes the workflow that we used at my prior job.  Nicely done Jim.

    Fitnesse Implementation Strategy
     

    We were able to almost completely test an asynchronous messaging broker inside FitNesse acceptance tests.  We also used it extensively to describe business validations and routing logic within the larger system.  The office evaporated before we retrofitted the rest of the application with automated test coverage, but we were well on our way. 

     

     

  • Maintainability is "ility" #1

    Occasionally, ok often, I'm gently mocked for the length of my posts.  I start with good intentions of making short, pithy Jason Yip-style posts, then think of something else I want to say and 10 pages later I manage to hit the publish button.  This is one of those that got away from me.

    I'm writing this post for a reason.  Specifically, I'm seeing a lot of harm being done on my project by focusing narrowly on performance optimizations and neglecting maintainability.  This is an ongoing discussion and debate for me at work, and I suspect it's going to continue to be an issue as long as I work in the financial world.  I think it's important to win this argument, or at least gain some concessions, so I would be very happy to hear everyone else's thoughts on this subject, especially folks that disagree with me.  In the course of this post I'm going to criticize the decisions and code of some of my teammates (and myself) on my current project.  It isn't that I think they're doing a bad job, just that there is a very real opportunity to do better.

    When you're architecting a software system, you must understand what the needs of the system really are and act accordingly.  It's important to understand the desired qualities of a system because software design often involves making compromises between opposing qualities.  For example, performance and scalability are often very much at odds -- you can generally only optimize on one or the other.  The point I'm trying to make is that you absolutely cannot focus completely on one quality of your software without considering the consequences to the other "ility" qualities of your system.

    In the end, you need to be optimizing on the qualities that genuinely create business value -- and I believe that the single most important quality for delivering business value on most projects is maintainability.  Any deviation from a more maintainable solution in favor of performance or security or scalability or whatever is dead wrong -- at least until proven otherwise.  Even if you do have stringent performance or scalability targets, I'm going to argue in this post that focusing on maintainability first will get you to those very same performance or security goals more efficiently in terms of development time.

     

    Early Performance Optimization did not Work

    To use a concrete example that I'm dealing with on my current project, the back end developers are consciously coding to minimize the number of IL instructions in an attempt to improve performance.  They're very concerned by issues like auto-boxing and the number of objects being created.  In fact, it seems to be their main judgement of code quality. 

    So our code is fast right?  Well, no, but we're working on it and making strides.  When we started integrating the client and server we found that marshalling data from the server to the client was extremely sluggish.  A little bit of profiling from one of my team members showed that we had a fairly severe bottleneck in our transport layer that totally dwarfed the run time of the rest of the system.  The IL instruction optimization in the server side code didn't particularly achieve anything.  What makes me angriest at myself is that we flirted with a more common approach to integration that I think would be more maintainable in the long run, but went with our current strategy in no small part because we thought it would be faster (sic).  I change my mind, what I'm really livid with myself for is not forcing the backend guys to benchmark the more maintainable approach first before we committed to this path*.

     

    Neglecting Maintainability is an Opportunity Cost

    The server side developers spent time making optimizations that might, or might not have, made some minor improvements in performance.  We collectively made some wide ranging architectural choices for performance that have not, in my opinion, added any value whatsoever. 

    I've got a major problem with the previous statement.  You only have a finite amount of time and resources to throw at your project.  Yeah, you can crank up your hours for short bursts, but there's always a cost for doing that.  To be truly successful, you should strive to spend these finite resources on things that add the most value.  The time spent on the server side architecture for performance bothers me a little bit, but the opportunity cost from not writing maintainable code or automated tests on the server side has been far more significant.  From Wikipedia, Opportunity Cost is "the cost of something in terms of opportunity forgone." 

    The end result?  While gaining essentially nothing in performance, we cost ourselves the opportunity to have worked more efficiently with that code in terms of both developer and project time by neglecting test automation and well factored code.    What we have is code that is genuinely hard to follow and spot errors through inspection because the methods are too long, with deeply nested if/then and looping constructs for good measure.  The existing code is much harder to change than orthogonal code backed up by unit tests would have been.  Surprise!  That server side code had to be changed in the very next iteration to add new features with additional changes looming for later iterations.  If I hadn't spent a couple of days slicing that code up with IntelliJ's automated refactoring support, we could have very easily ended up with code duplication in areas that have a high potential to change in the future.  Nothing makes extension harder than having to code the same rules in multiple places (a. More work and b. Greater chance of screwing it up).

    Even worse is poor coupling and cohesion properties that have defeated our attempts at writing isolated unit tests.  I'm all for integrated FIT style tests, but that shouldn't have to be the most granular testing that you can do on a system.  One of the lessons I've learned by dealing with so much legacy code in the last 2 1/2 years is that coding throughput is very much effected by the granularity and quickness of the feedback loop.  Writing small, granular unit tests that execute quickly leads to better productivity than the much slower feedback cycle from more coarse grained integration tests.  Having to fire up the UI to test something by hand is even slower yet.  I will very confidently claim that debugging time goes up geometrically with the coarseness of the testing, and that's significant because debugging is a major drain on developer productivity. 

    Just to beat this horse into the ground, the system we're building doesn't even have any realistic need for high performance.  The data sets are small, and the transaction complexity is fairly mild.  What we *do* need is reliability, but the stateful socket connection integration scheme we adopted in the name of performance has added complexity in the way that we deal with server connectivity.  I think a stateless connection model, while arguably slower, would have provided more value in terms of business value.  While surely improving performance, the proprietary binary formats we use for communication come with the opportunity cost of decreased interoperability, and hence a very real reduction in business opportunities.

     

    Ok smart guy, now my code isn't fast enough!

    Back to performance again.  So you concentrated on producing the correct business functionality first, with maintainability in mind, and it turns our that your architecture isn't responsive enough, or maybe can't handle the volume, or just that the user interface isn't responsive enough.   I'm not really addressing performance optimization and profiling in depth here, but take a quick read through Jeff Atwood's post Why aren't my optimizations optimizing? and you'll see that performance tuning is a tricky business.  There are too many conflicting variables to solve the problem through pure deduction alone.  Dollars to donuts, I bet you that some of the performance optimizations being done by my colleagues ended up hurting performance instead.  The point being that you almost certainly need empirical measurements to measure a range of trial solutions to arrive at better performance.  

    To drive this point home, let's say that your performance bottleneck is in the communication between physically distributed subsystems.  Forgetting for a minute about the cost of making changes to your code base, what can you do to make your system faster? 

    • Use lazy fetching techniques in fetching parent/child aggregate data structures to avoid fetching the child details when you don't need them. 
    • Use eager fetching techniques in fetching parent/child aggregate data structures to make fewer network round trips
    • Minimize the number of network round trips because that often makes the system faster by compressing the data sent over the wire or gathering data into a more coarse grained Data Transfer Object
    • Maybe the fancy compression or transformation of the data is eating up resources.  Change that to something else
    • Use more background threads
    • Eliminate thread swapping by cutting down the number of threads
    • Cache shared resources
    • Eliminate thread synchronization slowdowns caused by shared resources
    • a time to cast away stones, and a time to gather stones together; a time to embrace, and a time to refrain from embracing... (sorry, couldn't help myself)

    Wait, some of these changes contradict the other.  Which one is right?  Are you sure?  It could easily take you several attempts to find the right recipe for performance (or scalability or usability).

    You definitely need to make changes to improve performance, but those changes cannot break the functionality of the application.  Fast, buggy code isn't an improvement on slow, functional code.  If you've written maintainable code that exhibits orthogonality, you should be able to contain the changes to isolated modules without spilling into the rest of the code.  If you've built a maintainable software ecosystem of full build automation and solid test automation, you can drastically reduce the overhead of staging new code to the performance testing environment with less risk of breaking working code.  In other words, the things you do for maintainability should have a direct impact on your ability to efficiently make empirical

     

    Conclusion

    There are two general themes I wanted to explore in this post.  The first theme is just yet another cry for YAGNI.  Try not to invest time or effort into something that isn't warranted.  Make any piece of complexity earn its existence first.  A lot of this thinking is based on the assumption that it's easier to add complexity to a simple solution when it's warranted than it is to work around unnecessary complexity.  I'm also making a large assumption that you can make optimizations later if you've taken steps to flatten the change curve.  The second theme is that I think a deliberate focus on maintainable code structure and solid project infrastructure is a more reliable path to quality optimization than early optimization.  If your code and project infrastructure facilitate change you can always make adaptations to improve your other "ilities" -- assuming that you're paying attention as you work and make adaptations in a timely manner of course.

    And, by the way, maintainability is still the most important code quality.  Your system may not have to be blindingly fast, or scale like eBay, but it will change.  By all means, go learn about Big O notation and delve into the inner workings of the CLR (I'm finally reading the Jeffrey Richter book this week myself).  There's a very important point I'm trying to make for anybody engaged in building software, and that is that focusing on maintainability first is very often the most reliable means to get to exactly the other qualities that you need.  Ant if you write code that can't be maintained or changed, you're probably on a path to failure.   

     

    Wait, there's going to be more.  The next post in the "Maintainable" software series is going to be about the DRY principle and the Wormhole Antipattern.  First I'm going to give StructureMap a serious DRY'ing out for awhile, then I'm going to come back and tell you how it went.

     

    *I believe that the decision that ultimately led to our performance and tight coupling problems was based far too much on a "Sunk Cost."  More on that someday. 

  • The Anti Team

    EDIT:  Ayende's Anti Team additions is funnier than mine

    We talk a lot about how to structure great teams -- defining team roles and the qualities that we want team members to have.  Just out of pure mischief, and to fill a request from a friend, let's turn this around and I'll describe my vision of the worst possible team role structure.  Just in case you're wondering, this is an amalgam of a lot of different projects over the course of this decade and is not pointed at anybody in particular (ok, that's a bald-faced lie.  How about not pointed at anyone that is likely to be reading this).  Here you go, my Bizarro team-->

    • Non-coding Architect:  I fully believe that I can do a much better job designing and architecting software as a developer involved in the day to day construction of the code than a non-coding architect of equivalent skill.  Put it another way, designs get better faster when the designer has to feel the pain from clunky designs and adapts to alleviate that pain.  And frankly, I haven't come across too many non-coding architect's that had strong design skills when it came time to do detailed work.  You might say to me that the architect's job is simply the high level architecture and that the tech lead or developers should fill in the fine grained details.  I think that's mostly bunk because the fine grained "details" have an awfully lot to do with what the architecture should be in the first place.  I think the non-coding (or Powerpoint) architect position is a refuge for scoundrels.  Non-coding Architect is an order of magnitude worse when he/she is a member of a centralized architecture team that is the "keeper of the flame" for the enterprise architecture.  Traveling in packs reinforces the worst tendencies of Non-coding Architect guy.  For the sake of full disclosure, I was briefly a non-coding architect and have often clashed with non-coding architects.
    • "Spec" Coder:  The coder who will faithfully muddle through whatever design specification you put in front of him without deviation.  He's useless without a spec, because that's just not his job to design.  Much worse in my mind is the fact that "Spec" Coder will code without thinking or providing feedback to whomever is doing the design.  Non-coding architect plus spec coder is the worst possible project team permutation.  Being a "spec" coder is a fate that you need to avoid.  It's not a particularly fulfilling role, and the job security is not great.
    • UI Guy/Middle Tier Guy/Database Guy:  Dividing a team by horizontal layers has to be the worst possible developer team structure.  Too much specialization in a team is a bad, bad thing because your team becomes brittle when people specialize.  Think about this very realistic scenario, iteration #1 involves a lot of user interface programming but iteration #2 is almost completely about the backend.  Do you really want the backend developers idle in iteration #1 and the GUI folks idled in iteration #2.  When I'm a technical lead I've always found it more difficult to deal with specialists because their focus is so narrow.  I've always thought that specialists need a lot more hand holding to perform development tasks because they so frequently don't understand the context.  I'm a huge believer in "whole team" mechanics and purposely blurring project roles. 
    • Legacy Guy:  AKA, the "Bottleneck."  You've got to talk to the legacy code at some point, or even worse, modify it.  This guy is the one guy in the entire company who knows how to sacrifice animals correctly to change the legacy application.  He's spread across many different projects with the same needs, so getting his attention is always difficult.  Making matters worse, he usually doesn't speak the same language you do, and the cultural referents between the way he builds code and the way you do are very sparse.
    • Microsoft Project Project Manager:  This is the Project Manager whose world is completely bounded by and shaped by his Microsoft Project file.  Besides being extremely annoying, MPPM is completely incapable of adapting to changing conditions or even understanding that conditions are changing.  MPPM's primary tool is the "embarrassment meeting" where the entire team gives their actuals and gets called down on the carpet one at a time to be balled out for missing the original estimate.  Personally, I want my PM to be a road grader that gets out ahead of us and eliminates road blocks.  MPPM doesn't get that proactive style of management because they're too focused on the trappings of project management.  When I was an engineer on large petrochemical projects (250+ million), project controls and scheduling was a separate function from project management.  There might be something to that.
    • Out of Sight Testers:  Testers who are very isolated from the rest of the development team, usually coming onto the project at the very last moment.  I know that some people are adamant that the testers must be independent of the developers to the point of being completely isolated, but think of the cost.  Testing is about removing defects, and that is going to be much faster when the developers and testers are corroborating very closely.  Developers can fix bugs faster when the testers demonstrate the reproduction steps and test automation efforts go much smoother with developer involvement.  Think on this too, when you use waterfall testing you always have a ramp up time for the testers to understand the system, and you *always* tussle with the testers because they arrive at very different interpretations of the specifications than the development team did.  You can dramatically cut down that mismatch in understanding by having the developers and testers working together throughout the project.  Moving testers offsite is even worse.
    • Func'ky Spec Business Analyst:  The BA's that drop off a specification document and then disappear.  Any requests for clarification are automatically addressed by "didn't you read the spec?"  Grrrrrr.
    • Disinterested or Hostile Customer:  One way or another they're either paying for the project or they've got a large stake in the project's success, but they aren't the slightest bit interested in helping you.  Disinterested customer just doesn't want to be involved in the software.  Hostile customer thinks the best way to succeed is to administer regular beatings to the development team.
    • The Process Cop:  Somewhere, there's a group that is in complete charge of determining and enforcing the Process that all development teams will use.  They don't have to dogfood their own process, so they've got no incentive whatsoever to streamline things.  Chances are very likely that they haven't ever used the Process, but they've been to lot's of conferences.  They're also not particularly interested in your input, because that threatens their job.  One of many things that bugs me about CMMI is the Process Cop team.
    • Agile Zealot:  Yep, I'm willing to bomb on my side too.  Most of the antipattern role definitions are a side effect of bad waterfall thinking, but a truly dogmatic Agile Zealot is eXtremely unpleasant to work with.  If your judgement of right or wrong approaches is completely based on what's more XPish, you're likely to make bad judgement calls.  Actually, for that matter, XP or Scrum generally encourages and requires a very adaptive approach to how the team performs it's work.  If you turn off the adaptive aspect of XP/Scrum you've lost most of the advantages.
  • Practice Like you Play

    One of the homilies I had rammed down my throat by high school coaches was "practice like you play."  In the world of sports it just means to develop good habits and a high effort level in practice so that it carries over into the real games. 

    I fixed a bug yesterday at the last minute of our iteration that got through our automated FIT testing on the service layer because the automated tests were written at too low a level.  It would have been more work upfront, but if I had made the FIT tests exercise the service layer the exact same way as the user interface, I could have saved time in the end.  Lesson (re)learned.

  • Safety versus Power

    Dean Wampler wrote a post called Protecting Developers from Powerful Languages that reminded me of a running conversation I have with one of my client developers on "pointy tools versus idiot proof code." 

    It all started when I asked him to open up some constructors on the pseudo domain classes so we could more easily write unit tests involving those objects.  He had a concern that developers would use these "testing" constructors and the persistence layer would then crap out.  When he asked me how we could make idiot proof code my answer was the hiring process.  Some other answers might be mentoring, knowledge sharing, and streamlining your API's (!).  At the end of the day, I don't think there is any possible way to succeed if you can't trust your developers.

    A related concern that I've wrestled with all of my career as a technical lead is whether or not you limit the sophistication of your development approaches to the existing skillset of your team.  Sometimes hitting your teamate's ceiling might just be a sign that your design ideas are unnecessarily complex and unworkable.  Other times you should plow ahead with the knowledge that you'll need to invest in your team members.  Sometimes you just punt because you simply cannot succeed with a design that your team as a whole can't grok.  If you want the real answer you gotta go find someone wiser than me.

     

  • Size Matters

    Unnecessarily long methods and overgrown classes are a huge pet peeve of mine on software projects.  Apparently Bob Koss thinks so too in Size Matters.

    One of the points he makes is that more classes and more methods often equate to simpler code.  Amen. On a project several years ago I worked with a client developer who was not exactly enthusiastic about working with a team of consultants.  His big complaint about our design was that he could do the whole thing in 2-3 TCL files.  I still shudder to think what that would have actually looked like.

    By the way, the ObjectMentor blogs moved to http://blog.objectmentor.com.
     

  • What about Usability?

     

    Sort of a continuation of the ongoing maintainability series, it's time to look at some of the benefits.  And rant because that's just what I do.

    After publishing My Programming Manifesto (about the things *I* was thinking about at the time) post, Scott Watermasysk quite correctly stated that usability was very important and completely missing from the manifesto.  The first thing that popped into my mind upon reading Scott's post was "...it was NOT the perfect country and western song because he hadn't said anything about Momma, or trains, or trucks, or prison, or gettin' drunk" (if you understand that reference, you are *definitely* a redneck, and I, of course, proudly had the CD in college).

    So, back to the title, what about Usability?  Personally, I'm not very good with usability or GUI design in general, but what I do know from experience is that good usability and happy end users generally result from lots of user and BA feedback early and often throughout the project.  Getting feedback is the first step, but it's worthless unless you can actually act upon that feedback.  What I do know is how to write software that is maintainable and apply development practices that further enhance maintainability.  In bullet point form, here's how I think good code and good development practices facilitate good user experiences through the successful application of user feedback.

    • Slice the development work vertically instead of horizontally.  This was one of the hardest lessons I had to learn when I was new to Extreme Programming.  One of the constraints in defining a user story is that it must be a unit of work that provides observable value to the end users.  Don't build the data layer or only infrastructure first.  Write features from end to end and get these features in front of users as quickly as possible for early feedback.  The feedback from users on the very usefulness of the project is probably more important than understanding whether or not an architectural choice is technically viable.  In the former, you may need to cancel or reconstitute the entire project.  In the latter you merely need to find a different technical strategy.  Okay, in the latter you may need to cancel the project too.  It's disappointing sometimes that black and white rules just don't seem to exist.
    • You need to be able to push incremental changes of the system for demonstrations to the users, analysts, and designers quickly, reliably, and repeatedly.  A solid Continuous Integration infrastructure and strategy is the best way I know of to make rapid deployment of development builds possible.  You need a maintainable software ecosystem.  Invest in this infrastructure early, because it'll get harder the longer you wait.
    • From Scott's post:

    "You need to think early and often about how users will use your software and make sure the code can accommodate that experience and not the other way around. It is definitely possible to write killer code and then apply it to a killer UI and then refine both as necessary. However, real world schedules and business practices make this a utopia."

    Actually, to answer Scott's quote here, look back at bullet number one again about building complete features first.  Simply don't allow any divergence in schedule between the user interface and the rest of the system if you can possibly help it. 

    I'm going to partially disagree with Scott about the code though, or maybe just specifically define "killer" code to mean code that is easy to understand and change.  Say you've just finished a batch of features and successfully demonstrated these features to the users or user representative.  After using the early version of the UI, the users ask for some changes to the user interface -- even though these same people might have quite happily "signed off" on the UI design in some sort of specification or even a mock up.  You do want to make your users happy after all, so you have to break open that existing code and change the user interface without breaking the user interface.  A meticulous attention to separation of concerns (some flavor of Model View Controller) in your user interface code can isolate those changes, and make the code changes much smaller than they would be if you jumble display, behavior, navigation, validation, and business logic all in one massive file.  I really do believe that writing orthogonal code will help you create more usable code by giving you much more ability to safely adjust your user interface without breaking its behavior.

    • From Scott again:  "If people love your software they will ask for more...which is when having unit tests/etc becomes that much more important."  Ditto, and loudly.  Automated tests, and preferably multiple layers of automated tests from the unit level to acceptance testing, do a great deal to enable change.  Not just changes after your first release either.  Automated tests will enable you to more safely change code to accomodate the usability feedback you get from the user representatives during the project. 
    • There's a persistent, and false, rumour that you cannot write automated tests for the user interface.  You can automate testing for the user interface, but the conventional wisdom and my own experiences say that the best way is to remove as much of the user interface functionality away from the actual view machinery.  It's worth your time to go learn about, and apply, design patterns like Model View Controller and Model View Presenter to separate the concerns (validation, behavior, flow control & navigation, security, etc.) within your user interface layer.  The secret to testability in the user interface layer is to isolate as much as possible into "Plain Old Object" classes that are easy to test while keeping your View classes as thin as possible.  You can apply tools like NUnitForms, NUnitASP, Selenium, and Watir to test the actual UI, but again, you're probably better off if you have sliced the View code thinly so you can write granular tests against a user control at a time.  End to end testing through the client is an order of magnitude harder than writing unit tests.
    • Go read up, or listen to, any paper, book, or podcast from 37signals.  My short list of most admired software companies, besides Finetix of course, goes something like 37Signals, ObjectMentor, Google, and JetBrains.  One of my former employers is an obvious omission to that list, but I'm subscribing to the Groucho Marx theory on them.

    A mini-rant here.  Don't treat the structure and design of the user interface code as less important than the server side.  UI's will need to change the same way that business logic does, and need the same qualities of orthogonal code and infrastructural support from build and test automation.  Moreover, user interface code, especially when it's heavily event driven, can easily be more complicated than the server side.  UI code is very frequently the largest source of bugs because it has to interact with extremely unpredictable modules (humans).  Don't put the very most junior developers on "mere" GUI coding.  You'll need just as much architecture work on the client to achieve a good separation of concerns as you do in the rest of the system.  I'm eagerly awaiting Martin Fowler's sequel to the PEAA book that deals with patterns for user interface clients. 

    Mini rant #2:  Don't fool yourself into believing that you know anything just because it's in a specification document.  All a specification document is is a snapshot of what somebody thought at the time it was written, and it's very likely you'll learn something as you go along that invalidates or illuminates part of that document.  By no means does the existence of a "spec" give you a license as a team to turn your brain off.  I'm very rapidly coming around to the increasingly common opinion that functional specification documents do more harm than good.  More on this later someday.

    Mini rant #3:  Code behind's in the .Net world are View code, not controllers or presenters, period.  Treat them accordingly.

     

    Wait, there's more!  I'm not sure about the timing or order, but the next two maintainability posts are going to be about tradeoffs between maintainability and performance optimization and a timely for me post on the "Don't Repeat Yourself" principle and the closely related Wormhole Anti-pattern.

  • eXtreme Tuesday Club in New York

    Just a quick bulletin, Mike Roberts is calling all New York area XP/Agile practitioners to the new eXtreme Tuesday Club starting this Tuesday, Jan. 16th.  The meeting announcement is here.

    I always got quite a bit out of the AgileATX lunch group in Austin, so I'm certainly hoping for a good turnout.  I'll be there assuming things are calm on Tuesday and I can figure out how to take the train/subway into the city.

  • Have you ever been here?

    I'm only an observer, thank goodness, but I've been there too.  One of the best parts of being a consultant is limited "opportunities" for doing support. 

     

    Irate Person:  Fix the problem in production now!

    Dev:  Ok, can I get access to the production server?

    Irate Person:  No!

    Dev:  Ok, so what was it that you were doing when the problem occurred?

    Irate Person:  I don't know, just fix it! 

    Dev:  ...sinking feeling in the pit of your stomach -- unless you left a back door open for exactly this case, even though you know that you shouldn't have

  • Automated Web Testing with Watin

    I've apparently been living under a rock, because this was new to me.  Via Hammet, there is a .Net equivalent to WATIR called Watin at
    http://watin.sourceforge.net/.  They've even got a Fitnesse fixture built for it as well.  I was pretty happy with Selenium when I used it last year and it has the key advantage of being cross browser, but I'll definitely give Watin a spin next time I do web development.

    Of course, I betcha Bret would tell you that the big advantage to WATIR is the Ruby language. 

More Posts Next page »

This Blog

Syndication

News

All opinions expressed here constitute my (Jeremy D. Miller's) personal opinion, and do not necessarily represent the opinion of any other organization or person, including (but not limited to) my fellow employees, my employer, its clients or their agents.

About Me

"Best Of" Compendium

StructureMap (Dependency Injection for .Net)

StoryTeller (Supercharged Fit)

Build your own Cab

TestDriven

MVP