I gave a presentation to my team on S.O.L.I.D. software development principles, talking about how these five principles enable us to achieve High Cohesion, Low Coupling, and proper Encapsulation in our software projects. If you're unfamiliar with the SOLID principles, they are: - SRP: Single Responsibility - One reason to exist, one reason to change
- OCP: Open Closed Principle - Open for extension, closed for modification
- LSP: Liskov Substitution Principle - An object should be semantically replaceable for it's base class/interface
- ISP: Interface Segregation Principle - Don't force a client to depend on an interface it doesn't need to know about
- DIP: Dependency Inversion Principle - Depend on abstractions, not concrete detail or implementations
You can find a lot of good info on SOLID via Robert Martin (the man who coined the acronym) and the Los Techies crew: The final slide in my presentation shows the before and after of migrating a small app that reads a file and sends an email, from being 100% coded in the Winforms code, to a SOLID code structure. During the summary / review, I related the five SOLID principles to High Cohesion, Low Coupling, and Encapsulation through these descriptions: Low Coupling: By abstracting many of our implementation needs into various interfaces and introducing the concepts on OCP and DIP, we’ve created a system that has very low coupling. Many of these individual pieces can be taken out of this system with little to no spaghetti mess trailing after it. Separating the various concerns into the various object implementations has also helped us ensure that we can change the system’s behavior as needed, with very little modification to the overall system – just update the one piece that contains the behavior in question. High Cohesion: This really is a direct result of low coupling and SRP – we have a lot of small pieces that can be stacked together like building blocks to create something larger and more complex. Any of these individual pieces may not represent much functionality or behavior, but then, an individual piece isn’t much fun to use without a bunch of other pieces. DIP has also allowed us to tie the various blocks together by depending on an abstraction and allowing that abstraction to be fulfilled with different implementations, creating a system that is much greater than the mere sum of it’s parts. Encapsulation: True encapsulation is not just making fields private and hiding data from external objects – but hiding implementation details from other objects, depending only on the abstractions and expected behaviors of those abstractions. LSP, DI, and SRP all work hand in hand to create true encapsulation in the new project structure. We’ve encapsulated our behavioral implementations in many individual objects, preventing them from leaking into each other – and we’ve ensured that the dependency on those behaviors is encapsulated behind a known interface. We’ve hidden the implementation details and allowed for any implementation to be put in place for that interface definition through DIP. At the same time, we’ve done the necessary due-diligence to ensure that we are not violating any of the individual abstraction’s semantics or purpose (LSP), ensuring that we can properly replace the implementation as needed. The end result of our effort has created a system that appears to be complex on the surface – after all, there’s now 13 blocks in our diagram compared to the original 2. However, the apparent complexity of this system is diminished by the simplicity of each individual interface and object. Many small, independent, simple pieces have been wired together to create a larger overall system behavior that can be described as complex. Yet any of these implementations can be changed and/or replaced – very easily. At some point, I'm hoping to post the entire slide deck and presentation / code, but for now I thought the summary that I sent to the team was worth sharing with the rest of the world.
Dan North (father of BDD) responded to some dialog over on the Google BDD group with a very insightful look at what the "Context" of a specification really is. For me, this was an eye opening post and I'm already seeing ways to improve my specification tests. His post is worth quoting in it's entirety: Let me describe where the idea of contexts ("givens") came from. We started out by writing each story on the front of an index card, and on the back we'd draw a line down the middle to create two columns. Then we would label the columns: "I do this" and "This happens". (I think it was Ivan Moore who first showed me this.) It's incredibly simple and it worked well for describing the acceptance tests. I do Y, and Z should happen. If it doesn't I'm not done yet. Once it does I can go to the pub! Then I showed that to my business analyst friend Chris Matts, and he said: that doesn't make sense. I do Y and *anything* could happen! I request cash from an ATM and it could give me cash. Or it could refuse because I'm overdrawn. Or it could retain the card and call the police! You're missing a context. So we evolved it into *Given X*, When Y, Then Z. So now "the context" is simply another way of saying "which scenario is this?". It's the scenario where my account is in credit, or the one where I'm overdrawn, or the one where the card was reported as stolen. Like on Friends where each episode is called "The one where...". This means the scenario titles are typically just a description of the context, and the givens set up that context. In other words, you discover the contexts as you describe which scenarios you are interested in (and more importantly which ones you aren't). Cheers, Dan Solid gold, Dan! and thanks for the insight!
Earlier today, I had a conversation with a coworker concerning dependency container, dependency injection frameworks, and the root dependency inversion principle. My advice in the end, was to completely avoid the use of DI tools until the team as a whole understands the cost, benefits, and potential pains of manual dependency injection (pain being relative, and usually a sign of a learning opportunity). Part of the conversation also revolved around what constitutes the complete overuse and abuse of DI or IoC tool - which I can easily speak about due to extensive personal, self-inflicted, love-affair-of-IoC induced pain over the last year. However, the one thing I could not speak to was the correct use of a DI / IoC tool, because I believe that I have never used one correctly - or at least, my limited experience in using one correctly is so limited, I can't seem to separate it from the incorrect use. I’ve heard other developers (Jeremy miller, jimmy bogard, and others) say that they want to see as little of a dependency container / injection tool in their code as possible. This gets to the heart of what I was trying to convey earlier, but I’ve never had a good understanding of where you would actually allow a dependency tool to be used under those guidelines. On the way home from work tonight, I had (what I think is) a small epiphany around the idea that the dependency container should be limited to two key areas: the various implementation specifics (UI, database, etc) and the application layer. I think the use in the implementation specifics, for whatever use they're needed, is valid since these implementations are not unit tested to begin with. But I would highly recommend limiting the container’s use in these scenarios, for the same over used, abused reasons that I'm so intimate with already. More appropriately, the application layer seems to be the appropriate location to resolve the dependencies by using the DI tool to instantiate the object that needs the dependencies, automatically resolving the dependencies for us – not requesting the dependency directly. The best example I can think of, off-hand, is a workflow coordination service in the application layer. Let’s say your workflow moves from FormA to FormB in a windows app. The workflow class would use the DI tool to instantiate the ProcessAPresenter, which would resolve the registered IProcessAView as a constructor dependency. Then when this form is done, the workflow coordination class would use the DI tool again to instantiate the ProcessBPresenter and resolve the IProcessBView (and ISecurityService and whatever else) constructor dependencies. The key here is that we are allowing the application layer to use the DI tool, and not the other way around – not letting the DI tool instantiate the application layer - and not using the tool as a simple IoC container to resolve dependencies internally from the object that needs the dependency. These are primitive, unverified thoughts at this point, and need to be taken as such. I think this is a good start for a correct use of a DI tool, though. Additional implementation experience within this model would help to expose additional constraints and allowed uses, I would imagine. How are you using your DI / IoC tools? What are your thoughts on the subject? Your pains, your joys, your sorrows? And from a purely selfish perspective - am I on the right track, here?
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.
|