There's been a lot of recent talk about what is "done" in the Lean / Agile development communities, and the primary focus of these discussions has been focused on individual features or stories and the need to get them completely "done" (dev, test, acceptance test, documentation, delivered to customer) before they can be considered done. I wholeheartedly agree with this philosophy, to the point where I active introduce painful elements of software development to my team members, because the work must be done before we can deliver to the customer. All that being said - I think we're missing out on some potential benefit by not applying "done" at various and different levels of our software development projects. For example, the concept of "done" can be applied to an iteration. What constitutes an iteration being "done"? It's certainly not just the time-box of 2 weeks, 1 month, or whatever our iteration length is defined as. However, that time box is still important. We don't want arbitrary lengths of iterations. So how do we know when an iteration is completely "done" versus just being over? My initial thoughts revolve around a pass/fail checklist, similar to the swim lanes (or kanban board, if you want to call it that) that our stories go through. At this point, I would likely include the following: - Iteration length passed
- All stories in iteration "done"
- Code reviewed (possibly part of story being "done")
- Software is in a stable, working state
- Software acceptance tested by customer (or customer representative / SME)
- Retrospective held
There's probably some additional items to include here - this is just my initial idea list. Like the user story "done" criteria, an iteration cannot be considered "done" until all of the items in this list have been checked off. We don't give partial credit for a story making it to "in test", when there are 2 more columns to move through - the story is not done until it's completely "done". The same should apply to the iteration. Why bother making an official "done" list for iterations? For the same simple reason we do this for user stories - transparency and visibility into bottlenecks and problems. It's easy for a team to ignore problems like broken software at the end of an iteration - "oh, it's just XYZ... i'll take care of that tomorrow" - when we don't have the same accountability as we do in our stories. By making the iteration a pass/fail set, we expose those problems for the world to see. This exposure is a great motivational tool - who wants to be caught with an un-"done" iteration because of a "simple" bug? Do you and your team have an iteration "done" list? Do you even have a user story "done" list? It's all about exposing weakness and waste, then eliminating it and creating the official iteration "done" list is just one more step along the path. Don't stop there. What other processes - higher or lower level - can we standardize and improve?
The one and only goal in software development is to provide useful, functional software that creates value for the customer or consumer. Period. End of story. Not open for discussion or opinions. If you want to deliver software that the customer or consumer wants to use, likes to use, and ultimately does use to help solve problems and/or automate business process, etc, then you must know what the customer or consumer values. In other words - you can't identify your software's 'value' if you don't know who/what ALL of your customers are. I'd put money down to say that most software developers - perhaps most software development companies - have no clue how many customers and consumers there really are for any given software project. The one consistent answer that I would expect from every developer or company is that the person, group, or company paying money for the software is the customer. This is 100%, absolutely true. In addition to the paying customer, though, there are many other customers or consumers that need to be identified. As a starter, consider the following list for the customers / consumers of your software: - The paying customer
- Integrated external systems
- API consuming systems
- Software Testers and Test Lab personnel
- Technical and documentation writers
- Software developers (the ones writing the code!)
I know, I know... how could a software developer possibly be considered a customer or consumer of the software that they are writing? The answer should be obvious - who reads and writes the code? who maintains the code and needs to understand how the code is structured so it can be maintained? If you don't believe that your software developers are first class consumers of the system that they are writing, I would bet that you have a horribly complex kludge of code that no one wants to work on. If your developers are considered first class consumers of the code they are writing, I would bet that your team is happy and is constantly working toward better code - simple, readable, maintainable systems that are fun to work on. So, who are your customers or consumers? What value do they need in your system and from your system? And, how are you and your team responding to those value needs (if at all)?
Derick’s (brand new, just thought of it, but is now elevated to ‘mantra’ status for me) golden rule of Acceptance Criteria: If it’s not usable by every team member, it’s not Acceptance Criteria. And I do mean every team member - Customers, Testers, Tech Writers, BAs, Developers, UX Designers, and anyone else on your team. You can specifying the technical or UI details in the story’s detail, but it’s not acceptance criteria – it’s Technical Criteria, or UI Criteria, or Test Automation Criteria, or … etc. Generally speaking, don't include these alternate criteria in the story detail – let the specific team members determine their specific criteria and record it how they need to (through unit test for devs, interaction design mockups for ui peeps, etc).
The entire team – BAs, Devs, UX, Testers, Tech Writers, Customers and Management (especially customers and management) – all need to understand that the first few iterations of a team that is trying to convert to an agile methodology, will be slow and painful. You won’t get as much done as you think you should. You’ll run into constant problems, questions, points of clarification, and unknowns. Iterative development processes expose every single problem that your team never knew it had, and more – and if you’re honest and working with integrity, you won’t whitewash the problems and try to hide them; rather, you’ll embrace the transparency that iterations create and use it to drive improvement in your team. Converting to Agile is painful and it’s embarrassing at time. There's no question that it’s difficult to convert – especially if the team has any significant habits or experience from previous projects (and who doesn’t, aside from entry level people). Work through the pain, solve the problems one at a time and continue to improve the process. Eventually, the team will find it’s groove and productivity will increase. Over time, if the team truly is honest with itself and is continuously solving problem, productivity should skyrocket and you’ll wonder how you ever worked without iterative / agile methodologies. The key is - don’t expect it to be perfect… ever… take one step at a time and never stop moving forward.
In addition to our 'Code Review Challenge' that I discussed recently, we have been using another retrospective game - 'Name That Standard'. The idea is fairly simple. At the beginning of any retrospective, we go around the room and ask each team member to list out a standard that we are using in our project. This can be any standard that anyone feels the team is using or needs to be using, including coding conventions, architectural patterns, business and project management processes, communication means, etc. The intention is similar to the intent of 'The Code Review Challenge' - socialization of the the system. Only in this case, we are applying the term 'the system' at a much higher level - our organizational and project management / implementation system as a whole. Some thoughts on running 'Name That Standard': - Require not only naming of the standard, but accurate description of the standard and an example of where it is used. Alternatively, you may want to list a brief description out on a projector or whiteboard and see who can name the standard being described.
- Encourage the team to think about the team as a whole, not just their area of responsibility.
- Encourage the team to list standards that they don't understand, so that the rest of the team can chime in with assistance and help the team member in question grow
- Don't just let people yell out standards. go around the room, one person at a time. we got a little chaotic at first, and it was hard to know who said what.
- Encourage people to ask questions like 'i thought we were using this standard, but i saw someone else doing that standard'
- Encourage the standards to become more and more low level and detailed, over time (over multiple iterations)
- Encourage the standards to evolve and change for the better, over time (over multiple iterations)
In our implementations of this game, we've managed to get a fairly good list of standards written down and also resolved some questions on what standards we actually are using. For example, we had 3 dynamic mocking frameworks in our code base - Rhino Mocks, Moq, and a home grown one that we lovingly called 'BrandoMocks' (named for our team member, Brandon, who wrote it for fun one night). The end result of that discussion was to throw out Moq and BrandoMocks, adopting Rhino Mocks as our standard for this project. Having done 'Name That Standard' in two consecutive iteration retrospectives, I think I can say that it has been very successful. The team as a whole is becoming more and more aware of the standards that we are using, and the individual members are beginning to contribute more and more to the standards. With each passing iteration, and each new technique for creating collective ownership and socializing the system, I see the team coming together and really forming a team vs. a bunch of individual developers working on the same system.
My team tried out 'The Code Review Challenge' in today's retrospective. Overall, I think it went pretty well for the first time that we've tried this. Here's some initial thoughts after our first attempt. - Make sure the team knows what's going on before you start. I was not leading the review, and I had to step out right when it started. I believe the team lead explained the idea, but I'm not sure I had fully explained to her. In the end, ensuring that each team member is comfortable with the idea and the process is important.
- Encourage discussion with the group during the review. Don't force the talk from the reviewer, but ask questions that will help lead them to conclusions and descriptions of what is going on.
- Don't expect everyone on the team to be able to recognize the same standards, practices, or issues. each team member will have a different understanding of the standards that we are trying to apply, at any given time. encourage learning help others to see what you see.
- make sure everyone on the team has a chance to review someone else's code. i think we ended up with a good mix of people reviewing and being reviewed, but it wasn't planned, it just kind of happened. next time around, we're going to ensure that it happens by planning ahead based on the work done by the people in the review.
i think there was a lot of benefit in doing the reviews in this manner. we ended up with a lot of good comments, suggested improvements, etc. i'm happy to say that the code review challenge was a success and that we'll continue to do it in future retrospectives and reviews.
A Food Network Challenge During this season's 'The Next Food Network Star', there was an episode where the contestants had to cook a dish and review it in front of a camera, describing it's visuals and taste. The twist to the challenge was that they were assigned someone else's dish to review, after everyone had already cooked. Once the assignments were made for who was going to describe who's dish, they were not allowed to see the dishes anymore. A given contestant was then put in front of a camera and had their assigned dish uncovered in front of them. From that point, they had 90 seconds to describe the dish that was in front of them - that included figuring out what it was, what it looks like, what it tasted like and what it smelled like. This was a rather tough challenge to watch, and a few of them didn't do so well. A Code Review Challenge What if we take the Next Food Network Star's challenge and apply it to code reviews? When we sit down in a code review session, rather than having the person who wrote the code doing the driving and telling us what is going on, have someone who has not seen the code do the driving - read the code, read the unit tests, etc - and describe to us what is going on. 90 seconds wouldn't be nearly enough time, though, so we'd have to change that time limit based on the size and complexity of the functionality being reviewed. Other than that, though, it seems like it would be a great way to test the team's ability to pick up an area of the system that they have not seen before and quickly learn it so that they can work in it. Standardized Work - Judging Code And Code Reviewer There are 2 real benefits to the code review challenge, as I see it: - Judging how well did the coder apply our coding and development standards
- Judging how well the reviewer can recognize and describe the standards being applied
We are now building our system from User Stories, with Context / Specification style unit testing. One of the intents of this style of unit testing is to provide an easy-to-read and easy-to-learn set of examples for developers that need to know how to do something or how something works in the system. If the unit tests are properly written and expressive of the code's intent - it should be really easy to see what's going on. If we have good standards in place, a developer should be able to find the code in well organized areas, easily, and know where to look for the various components and parts. It seems to me that the code review challenge would help to prove a number of these key factors in our projects and our teams: - Coding and architecture standards
- How well the standards are known
- How well the team follows the standards
- Expressive Code
- How easy is it to read the code
- How easy is it to understand the code's intent
- Maintainable Code
- How easily can the code be changed and extended
- How quickly the team can socialize and collectively own the system
A Challenge In Name, A Learning Experience In Practice I would avoid applying any sort of point system or other reward / incentive for the code reviews. It seems that this could easily backfire and cause junior team members (in skill or time on the team/project) to feel intimidated or feel that it's an unfair process comparing them to the senior level team members. The intention of the code review challenge is not to cause dismay in any team member, but to create a learning environment by continuously challenging every member of the team. Ultimately, the code review challenge should foster the socialization of the system and lead to a strengthened sense of collective ownership, resulting in a better system. Nothing New Under The Sun? With all that being said - I'm doubtful that I'm the first person to ever hold a code review like this. I'd be interested in knowing if there's any standard development practices (Agile or otherwise) that are heading in the same direction. Any advice or opinions of you, the reader, would also be very welcomed. If you know of a resource that describes the same basic ideas or if you have any input to help me improve the idea, please leave a comment and let me know.
Some coworkers were recently working on an object model for a simple security system. After some discussion with them, we came up with this basic model: A permission is defined as an activity that can be assigned to a user, or group, and can be allowed or disallowed. From a Domain Driven Design perspective, we're stating that the Permission is the aggregate root. The User object itself, while involved in this aggregate, is divorced from this aggregate's relational model - you can load and work with a User object without having to load or worry about the Permission hierarchy. The addition of the "UserGroup" is solely for the many-to-many relationship between User and Group mappings with NHibernate and is not actually part of the object model's code. Once we had this model in place, we wanted to have a simple query that allowed us to load a given permission object by activity name, for a user - whether the user was assigned directly or via a group. At a high level, this model and query should be fairly simple to work with. It turned out to be a massive learning curve in NHibernate, though. After much trial, error, and Google searching, we ended up with this NHibernate query code: ICriterion userIdMatches = Restrictions.Eq("Id", userId);ICriterion activityNameMatches = Restrictions.Eq("Name", action);ICriterion userIdAliasMatches = Restrictions.Eq("u.Id", userId); DetachedCriteria groupPermissionCriteria = DetachedCriteria.For<Permission>() .SetProjection(Projections.Property("Group")) .CreateCriteria("Group").CreateCriteria("Users").Add(userIdMatches);ICriterion groupSubquery = Subqueries.PropertyIn("Group", groupPermissionCriteria); DetachedCriteria permissionCriteria = DetachedCriteria.For<Permission>() .CreateAlias("User", "u", JoinType.LeftOuterJoin) .Add(Restrictions.Or(userIdAliasMatches, groupSubquery)); permissionCriteria.CreateCriteria("Activity").Add(activityNameMatches); ICriteria executableCriteria = permissionCriteria.GetExecutableCriteria(Session); result = executableCriteria.List<Permission>(); return result;
There were a lot of lessons learned and a lot of parts that eventually got put together. Here's a quick run-down of what we ended up with and why.
- The ICriterion's at the top of this code block are there to provide a little better readability in the real query code below.
- The groupPermissionCriteria is set up to find a permission object where the specified userId belongs to a Group that belongs to the Permission - i.e. find permissions where the user is assigned via a group. The learning curve from this perspective was the SetProjection call. Though we are not entirely sure what a projection is at this point, we did find out that it was necessary for us to set this projection so that the detached criteria could be used as a sub-query.
- The groupSubQuery is the conversion of the groupPermissionCriteria into an ICriterion so that we can do a logical Or with it in the primary query construction.
- The permissionCriteria object sets up the core criteria logic and ties together the group permission assignment with the user permission assignment.
- CreateAlias is used so that we can shorten the criteria for loading by the User assignment directly. The JoinType on the alias needs to be Left Outer Join so that we will return a proper Permission object even when there is no direct user assignment.
- After creating the alias, we can add an "Or" criterion to the query and specify that we want to match based on the user's direct assignment or a group's assignment.
- The last line of the criteria simply adds the Activity criteria to load by the activity name.
The resulting SQL will load the permission by Activity name AND (User assignment OR group assignment where the user is part of the group).
SELECT this_.PERMISSIONSID as PERMISSI1_0_3_, this_.IS_ALLOWED as IS2_0_3_, this_.ACTIVITYID as ACTIVITYID0_3_, this_.USERID as USERID0_3_, this_.GROUPID as GROUPID0_3_, activity1_.ACTIVITYID as ACTIVITYID4_0_, activity1_.ACTIVITY_NAME as ACTIVITY2_4_0_, activity1_.DESCRIPTION as DESCRIPT3_4_0_, activity1_.INACTIVE_DATE as INACTIVE4_4_0_, u2_.USERID as USERID3_1_, u2_.USER_NAME as USER2_3_1_, u2_.INACTIVE_DATE as INACTIVE3_3_1_, group6_.GROUPID as GROUPID1_2_, group6_.GROUP_NAME as GROUP2_1_2_, group6_.INACTIVE_DATE as INACTIVE3_1_2_ FROM PERMISSIONS this_ inner join ACTIVITY activity1_ on this_.ACTIVITYID=activity1_.ACTIVITYID inner join USERS u2_ on this_.USERID=u2_.USERID left outer join GROUPS group6_ on this_.GROUPID=group6_.GROUPID WHERE activity1_.ACTIVITY_NAME = :p1 and ( u2_.USERID = :p2 or this_.GROUPID = ( SELECT this_0_.GROUPID as y0_ FROM PERMISSIONS this_0_ inner join GROUPS group1_ on this_0_.GROUPID=group1_.GROUPID inner join USERS_GROUPS users5_ on group1_.GROUPID=users5_.GROUPID inner join USERS user2_ on users5_.USERID=user2_.USERID WHERE user2_.USERID = :p3 ) )
Using a sub-query to load based on the group is not the most optimal way of loading the permission for the group assignment. However, since all of the joins in the main query and the sub-query are done on primary and foreign keys in the tables, performance should not be an issue. The only real performance concern for this query is the activity name in the where statement. A simple unique constraint and index on the activity name, though, will solve that problem.
Test Driven Development Is To Unit Testing As Interaction Design (IxD) Is To Accidental Design One of the major problems with writing unit tests after the code is that it is very natural to write tests that prove the code works the way it was written, instead of the way it should work. Writing code test-first (via whatever flavor of Test Driven Development) flips that scenario right side up. TDD helps to ensure that code is written to work according to specifications and not the other way around. Similarly, designing and/or implementing a UI after a behavior or process has been coded is a likely to result in a UI that fits the code model and not a model that fits the needed interaction and workflow. This situation must also be flipped right side up - interaction design should be done before, or at least in parallel, with coding. It cannot be left to accidental or incidental happenstance - interaction design must occur with proper interaction patterns and practices in mind. Overlapping IxD and TDD To overlap interaction design and test driven development, there are a few key words that need to be borrowed from interaction design. Fortunately, they easily fit within TDD development techniques and philosophies. Epistemic work is exploratory in nature, or a process of trial and error through research. TDD and interaction design sketching are both epistemic. TDD explores API possibilities and allows easy trial and error to find the simplest implementation for what you need at the time. UI design sketches also allow you to quickly explore interaction designs - whether it's a white board, pencil and paper, graphics design software, or even quick-hack forms layout in IDEs. You can quickly and easily throw away a bad API in TDD and you can quickly and easily throw away a bad UI/Interaction design when you have nothing more than pencil sketches or white board drawings. Pragmatic work is very structured and step-by-step in it's nature, implementing patterns and practices to fulfill what is needed at the moment. Implementing Code after writing unit test and implementing UI after designing around constraints are both pragmatic. TDD is pragmatic in that you only implement what is needed to properly pass the tests that have already been written. Similarly, with previously designed interactions and UI elements, implementation can be easily limited to what is needed for the UI. With epistemic and pragmatic work covering both interaction design and test driven development, it seems that they are a natural pair. An analysis of a user story and it's acceptance criteria will create the unit tests that we need. At the same time, the same analysis can be applied to interaction designs. Additionally, a strong understanding of how a UI will look can have a profound impact on the code that is written, and vice-versa. Therefore, it is natural that interaction design and test driven development are done at just-in-time intervals - before the real work is implemented in the real code and UI platform. IxD As Part of "Done" Despite interaction design being a required part of UI development, not all user stories require a UI. Interaction design may not fit into a swim lane board or be part of every story's "done" criteria. However, interaction design will always be done for any story that does have a UI - it may simply be an accidental or incidental part of the software development process for a team, though. If it's safe to assume that the work will be done, then it is the team's responsibility to ensure that it is done correctly. Don't let interaction design happen accidentally or incidentally in the development process. Set a standard of always including interaction design in the development process, the same way Model View Presenter/Controller is a part of development.
Collective Ownership One of the principles that I’ve tried to instill in my team(s) is that we are a collective ownership team. I've talked about Collective Code Ownership before, and I still believe it is needed to have a solid team. My views have recently expanded a little to include more than just the software developers on the project team - crossing all functional boundaries in a given project team. Collective Ownership means that there is no one person on the team – developer, ba, tester, etc. – that "owns", controls, or is solely responsible for any one portion of the system. Code, documentation, and all other artifacts are owned by the entire team, as we should be working as a team to complete the project. Obviously there are people who specialize in one area or another, so they will have more influence in those areas. Depending on the corporate culture that you work in, there may also be some boundaries that we can’t cross due to politics, etc. At the very least, though, every last line of source code in a project is owned by the team and is the responsibility of the team. The moment any artifact (code, images, documents, etc) is committed to source control or any other common project storage, the person that wrote/created/modified it is no longer directly responsible for it – the entire team is, and anyone on the team that sees a problem is expected to fix it or call attention to it so it can be fixed. This principle is key to the team being able to create great software. We don’t want people to say "my code is better, and my area of the application works, but your code is bad and doesn’t work". This kind of divisive thinking will create a poorly constructed system that doesn’t fit together very well. We need to ensure that the entire team is saying "our code is good", or "our code needs work", and "I can help improve the team’s code base". Your Code Is Terrible Because My Code Is Terrible I’ve heard several times that a code base is only as good as the weakest developer on the team. I believe this is true – especially in a collective ownership environment. The moment a developer commits code, it is available for any other team member to modify, and therefore the quality of the code is immediately suspect and subject to the standards of the weakest team member. Despite the potential problems that collective ownership creates, its very nature should help to bring the skill level of every developer on the team up, alleviating the 'weakest code' problem. When a 'strong' developer sees the code of a 'weak' developer (those terms being relative to a lot of factors, of course) the strong developer should instinctively want to help the weak developer so that they are both producing better code. When a weak developer sees code that they believe is strong, they should instinctively want to mimic the qualities of that code and either ask for assistance or work to understand how to produce it on their own. Though it does take time and a lot of hard work, the end result should be that the collective weakness of the team disappears. The 'weakest' developer on the team will become an unknown – even irrelevant. Everyone will have strengths and weaknesses, but the team will balance each other out and the code that is delivered will be good. Socializing The Design For collective ownership to work, though, we have to ensure that the entire team is comfortable with the entire system. Each team member must be able to understand, update, and "own" any part of the system that they are currently working on (ownership being transitive, and forfeit once they commit the change, of course). For an entire team to be comfortable with the entire system, we need to socialize the design - that is, ensure the team understands the architecture and design fundamentals through the social aspects of working on a team. When we do a good job of socializing the design, it becomes much more natural for the team to collectively own the code. A given developer may not know anything about a given area of the system, but because there are good standards in place and everyone is aware of the overall design of the system, everyone on the team should be able to dig into any given area and learn it quickly. The real challenge in socializing a design is figuring out what works for your team. There are as many ways of socializing the design, as there are systems and teams - some of which include: - Code reviews (formal and informal)
- Pair programming
- Email, Presentations, and other “here’s how you do xyz”
- Wiki's, Forums, and other ways of asking “what do you think about xyz or 123”
- Iteration retrospectives
- Etc, etc, etc
No two teams or situations are alike, either. If you are limiting yourself to just a few of these techniques (or only the techniques listed here), you may not reach the full potential of your team's understanding. Try different combinations of socialization techniques and see what works well for your team. End Goal: Continuous Improvement In the end, a socialized design and collective ownership enable teams to work more efficiently by preventing the bottlenecks that 'I maintain XYZ' will create. Code quality will rise, the team work work together, and we can start to move forward into better standards, better practices, and continuous improvement.
|