|
|
-
My blog has officially moved: http://www.derickbailey.com Please update your subscriptions to my FeedBurner feed, if you have not done so already. AvocadoSoftware.com will likely stay around for a long time, so I can have my Google links, etc. still work - but I have no plans on using this blog anymore. Onward, and upward!
|
-
I'm considering a blog change (domain name and blogging system). Any suggestions for blogging system? Here's my basic requirements: - Use Windows Live Writer
- Insert pictures directly into post
- Paste CSS formatted code examples directly into post
- Use Feedburner to supply the RSS in the meta tags of the site / posts
- Post files for people to download
Not a terribly large list of requirements... this ancient version of CommunityServer that I'm using supports the first one, and the last one - and that's it. I don't want to move to Community Server 2007/8 at this point, because it's far too "functional" (re: feature bloated) for what I really do with my blogging. I've heard dasBlog and SubText are good. SubText is a fork of the original .Text (which became Community Server) so I'm at least familiar with it's original architecture. Other suggestions?
|
-
I hereby declare my love affair with Test Driven Development over, and my new infatuation with Behavior Driven Development beginning. Call me a cool-aid drinker, a bandwagon jumper, or whatever you want... I finally get it... When I write TDD / Unit Test code like this: [Test] public void WhenMagic8BallIsShaken_ThenAnAnswerIsGiven() { //insert code and asserts, here. } (and yes, this is an actual example of a unit test I wrote last week, during a TDD training session I was doing for some coworkers), what am I really doing? I'm not just doing TDD, I'm not just Unit Testing... I'm specifying the behavior of the system; I'm specifying the experience of using the magic 8 ball; and really, I'm only about one step away from the mechanics of Behavior Driven Development. It's an eye-opening realization for me, and gets me all kinds of excited and re-invigorated to learn more. Now, I'm not going to say that TDD is dead or invalid - it's more like saying I finally see how Domain Driven Design and Test Driven Development are truly married together... and then you change the semantics of the name to fit that marriage. It's also worth noting that this jump, for me, is purely from the engineering perspective so far. I still have no real experience with a true agile development project / process. Although I'm slowly stepping closer to that - the more I see in the engineering practices, the more I see the need for the project management practices. ... more to come, as I travel down this path.
|
-
I'm in the middle of rebuilding the architecture used for my build processes, and I'm defining separate configuration files for each environment: Local (developers box), Dev (development server), Test, UAT, Production. These config files are all stored in the Trunk\Deploy\Configuration folder in my subversion repository. One of the issues that we've seen in our projects, is that a developer is having to check out the entire Trunk of the repository so they can build - because certain files, like the config files, are stored in folders outside of Trunk\Code\. Low-and-behold; Subversion Externals. :) I go into my Trunk\Code\Project folder, right click add a subversion property named "svn:externals" with a value of "Configuration ../../Deploy/Configuration". I update my local copy of the Trunk\Code folder and magically, a Configuration folder appears with all of the correct config files for the correct environment. When I go to the Windows properties of this folder and look at the Tortoise tab, this folder is pointing back to the correct Trunk\Deploy\Configuration folder! My developers will no longer be required to check out anything but the code folder, to their local box; and they will be able to maintain and modify any of the config files that they need to work with. sweet.
|
-
If you're setting up Subversion and Apache, and you are not using VisualSVN Server, you're wasting time. A year ago, I spent 3 days learning how to install Apache, host Subversion in it, and configure all of the users for my system. Today, I downloaded VisualSVN Server (all of 6 megs), started an instance of my Win2k3 Virtual PC image, installed VisualSVN Server and added a repository and a user. Total time spent: less than 10 minutes from download to using my repository. Of course, I don't have LDAP authentication configured - but that's only another 20 or 30 minutes (copy & paste from existing Apache config files).
|
-
I'm tired of the old "dredge" moniker... tired of nobody knowing who I am and tired of some of the associations that this name has. So, I'm going to be posting as myself, from now on: derick.bailey
|
-
I follow a lot of Agile Engineering Practices, at this point in my career: Test Driven Development, Domain Driven Design, Single Responsibility Principal, Separation of Concerns, Inversion of Control, Dependency Injection, etc. To get to this point, I've basically taught myself - I read blogs, books, example code, and work my butt off to make sure I know what I'm talking about so that I can stay a few steps ahead of my team(s). In all this effort, learning, failure, success and otherwise, I've learned a few key things about software development as it relates to the Agile world. - Unit Testing is an absolute must, even if you don't do Agile
- Unit Testing is not Test Driven Development
- Test Driven Development is Agile Engineering, not Agile Process
- Agile Engineering alone, does not make Agile software development
1. Unit Testing is an Absolute Must Whether or not you are trying to do any sort of agile software development, or even test driven development; if you do not unit test your code, you are opening your code up for breaking unexpectedly. In the worst case scenario, you are preventing your code base from being able to change with the needs of the business and causing crippling problems by not keeping your code separated appropriately. Think about the goals of object oriented development: well encapsulated, highly cohesive and low coupled objects that can easily be re-used. These goals are very high, very lofty and they may be pie-in-the-sky dreams for some developers. So how do you achieve such goals? Single Responsibility Principle, Separation of Concerns, and Inversion of Control. All of these practices are much closer to the implementation level of code - they tell us to keep our code as small as possible; only write the code that needs to be written, in this class. When you see code that does not quite fit into the class you are currently writing, you look for a better place to put that code. This means that you need to split data access out of your business processing, user interface out of your business processing, and often times you will need to split your business process out into multiple classes. The hard part is, single responsibility, separation of concerns, etc. are not one-time coding practices per class. They are processes that require a continuous series of design, redesign, and refactoring of your code. Don't assume that an object you write, now, will be the same 10 minutes or 10 days from now. If you find yourself in a situation where two objects are very similar, you may have an opportunity to create a better abstraction that can fit both scenarios - whether it becomes a single object, a template pattern, strategy pattern, or other solution. If you are continuously redesigning your code to fit the business needs, and you are continuously separating your code into more and more classes, creating the high cohesion and loose coupling of true OOD; how do you manage the process and ensure that you are not breaking your application as you go? The answer is simple: Unit Testing. If you are not unit testing your application, then you do not know if the application works until you actually run it and try to use it. If your application is small - one or two classes, one or two screens - this may be reasonable and you may just run the application in it's entirety to see if it works. If your application is medium to large, though - one or two hundred or thousand classes and screens - you will quickly find yourself in a situation where you cannot expect to run the entire application in a reasonable amount of time and you are likely to miss some of the features and functionality if you do try to run it all. The only reasonable way to ensure that your code still works, is to have an automated test suite that can be executed on a regular basis. The easiest way to achieve this, at a very low level, is to do Unit Testing and ensure that your code is 100% covered with the unit tests. By unit testing every line of code in your system, you will have a rapid feedback tool that tells you when you break something. This rapid feedback will allow you to quickly fix anything that is broken, compared to the "run it and see" process. With all that being said, it's very important to understand ... 2. Unit Testing is not Test Driven Development Test Driven Development involves Unit Testing. In fact, Test Driven Development doesn't exist without Unit Tests. However, just because you are Unit Testing, that does not mean that you are doing Test Driven Development. When it comes down to it, Unit Testing is all about insurance - make sure your code is covered because change will happen, and change breaks things. Test Driven Development, on the other hand, is all about software design; writing your unit tests first, to drive the design of the software that you are really writing. TDD is not just Unit Testing; TDD is not just Test-First-Development, either. True TDD is the practice of designing the APIs in your code, by writing out the API that you want to see before you try and implement it. To do any sort of software development, you need to have some basic functional requirements - what is this software going to do, and why? When you start breaking these requirements down into pieces that are small enough to implement, you can begin to see how the software will look - what classes will be there, what processes will run, and how it all fits together. When you have good requirements it's easy for a developer to see some of the low level code implementation - using whatever design pattern to create these classes, with this code underneath so that this feature can call into them and do something... TDD, though, wants you to step back a few paces. Don't worry about the low level implementation details, yet. Let's focus on how you want to call into the implementation - what is the API that you want to call, in order to do these processes. Rather than looking at a requirement and saying that you don't know what the API will look like because they have not implemented any of the details, TDD forces you to think backwards - you don't know what the implementation is, but here is the API that you want to call. 3. TDD is Agile Engineering Only There is a huge disconnect from Test Driven Development, to a true Agile software process. Agile Engineering is a great set of practices that every developer should adopt. This includes Test Driven Development, Domain Driven Design, even Pair Programming. However, these engineering practices can and do fit into any software development lifecycle: RUP, Waterfall, iterative waterfall, and everything else you can image, can all have Agile Engineering practices put in place. I would go so far as to say that we shouldn't even call it Agile Engineering - rather, we should just call these good software engineering practices. To illustrate this - look at the projects that I work on. They are all standard waterfall type projects. We've implemented these engineering practices in every project that I have lead, for the last 2 years, though. 4. Agile Engineering is not Agile Software Development Real Agile software development is more than just good engineering practices. It encompasses the entire software development lifecycle - from requirements gathering to requirements definition, continuous discussions with the customer to elaborate on those requirements, discussions with the customer to determine what requirements go into what release and iteration, and more discussions with the client to ensure that the software is heading down the path that they actually want. Have you noticed a trend, here? There's a lot of constant, continuous discussion with the customer in real Agile development. This is an absolute must, in Agile work. If you don't stay in regular communications - as in daily, if not more often - then you don't know if the customer is getting what they want. I've worked on various projects that do some of these - short iterations, continuous integration for unit testing, regular releases to the customer, even pair programming; but I have never worked on a true agile project. It's just not possible to do true agile software development if you don't have complete buy-in and ownership from the entire team - from the customer down to the individual developer. I dream of the day that I'll have a chance to work in an environment like this. The Future of Agile Agile engineering, Agile process... how does this all tie together? Where is the knowledge moving and how do we get there? Personally, I'm on board with Behavior Driven Development group. Imagine taking every aspect of Agile, and wrapping it up into one very cohesive set of processes and practices. I get all kinds of excited just thinking about it. :)
|
-
Another interesting side effect of my conversations with Scott Bellware, today... I'm not sure if he agrees with these conclusions, but I certainly didn't come to them by my own thoughts alone. My understanding of the Test Lab or QA department in a software development shop has been based on the idea that the QA team members are not developers - and that they shouldn't be, because a developer will write tests that they know will pass. Well, as it turns out - the reason was correct, but the conclusion was wrong. In reality, a software development project needs to automate every level of testing - all the way out to the point where the actual customer is sitting down and using the software. This includes: Unit Testing, Integration Testing, and UI Testing. Whether or not you distinguish between these three levels is irrelevant; the testing of the system must be automated as much as possible. This leads us to the next question: What is an automated test? Typically speaking, it's code. Who writes code? Software developers. Do you see where this is heading? maybe? :) If software is developed by software developers (duh), why is the software that tests the software not written by software developers? Lets take this to the agile world of 2 week iterations (or sprints or whatever you want to call them). If the software requirements are being defined every two weeks, then the tests are being defined every two weeks as well. If we are automating the tests during these two week cycles, and the tests are written as code, then why are the software developers not writing the tests? The obvious answer is my reason above - the developer will write the test that they know will pass, because they wrote the code that is being tested. So don't let that happen - don't let the developer who is writing the code, write the integration / UI test code. Then, how do we let a developer write the integration / UI test code? Triples Programming. The standard XP / Agile practice currently has developers working in pairs - one driving the keyboard / mouse, and one thinking two steps ahead. I say we add a third role and a third person to the mix - one developer to write the integration / UI tests at the same time that the unit tests / production code are being written. What you CANNOT do, though, is include that third role in only two people. The moment you break down to two people, you need to remove the integration / UI test writing or you end up with the original dilemma. You also need to ensure that the developers in the triple, are swapping positions on a regular basis - make sure everyone gets a change to drive the keyboard, think ahead, and code the integration tests. By programming in triples, you get the advantage of specifying the integration tests as soon as the code is functional. You don't lose motivation to write those test, you don't lose clarity of what the functionality is supposed to be, and you don't have to re-discover the same functionality and clarity at a later time with a new set of minds (the QA team). So, what happens to the QA team? They are still needed, certainly. But now they can focus on their subject matter expertise - the human interaction of manual and random of software testing by using the actual application. i.e. the parts of testing that can't (or shouldn't) be automated. ... Ok, ok, ok... if you really want to stick with pair programming you can take this concept and assign 2 teams per story; 1 team to unit test / write production code; and 1 team to integration / UI test.
|
-
I had the pleasure of picking Scott Bellware's brain, today, about some questions surrounding Continuous Integration and build promotion. The results of the conversation have yet to be implemented, but I have a much better understanding of the situation, now. In my previous post, I was under the impression that the issue was purely technical or mechanical - I thought I needed some special software to automagically do a bunch of deployment related tasks and push my software up to the next level (Dev, Test, UAT, Production). As it turns out, I already have all of the tools, technical ability, and mechanics to solve the problem. What I was lacking, was the correct perspective. Rather than trying to dig my way through the mud that I've been so mired into, Scott was able to help me back out of the mud and turn around to see the clear waters in front of me. I now understand that I should not be *pushing* from Dev to Test to UAT, but rather I should let the person responsible for the target environment *pull* the build into their environment. It's amazing how a simple perspective change can open up an entire world of possibilities. I'll be sure to blog about my solution to the issue, as it becomes reality. Hopefully I can save a few others from the same torture that I've been putting myself through.
|
-
-
I was integrating a component from another developer on my team, recently, and I came across a common naming convention and event implementation. The naming convention included a "DataSource" property on a UI control and a an event with an EventHandler delegate signature. I have a hunch that the use of EventHandler and names like “DataSource” are there because this developer was following the standards that were built into the .NET framework. These standards are great when you are building generic controls in a UI layer and highly reusable frameworks where those controls can be applied across a large number of applications and business needs. These standards don’t give much value in the context of the business process the represent, though. When building business applications and controls for a specific business purpose, names like "DataSource" mean that I may have to look at the return data type to see if it’s something that I might be able to use, and may mean that I may need to look at the implementation to see if it’s really what I want. If a method or property name is geared towards the business need that it represents, it’s less likely that I’ll have to dig around to see if it’s what I need. So, rather than calling it “DataSource”, something like “WidgetUsageHistory” may be a more direct statement of what this property really represents. Similarly, a business process related event could provide a method signature like this: public delegate void WidgetCreatedDelegate(Widget widget); public event WidgetCreatedDelegate WidgetCreated; This would have better business value in the code, because it tells me that I can subscribe to this event and be notified of a Widget being created, and know which Widget was created.
|
-
I've spent a lot of time, over the last year or so, working in Model-View-Presenter code. During this time, I've had a lot of questions; come up with a lot of different answers to the same questions; implemented a lot of different infrastructures for MVP including auto-magic view instantiation, manual view and presenter instantiation; and changed my mind about how best to implement certain portions of MVP a thousand times. Through all of this, I've come to some fairly good conclusions and a set of best practices that are helping my team(s) build better software, faster. My current project list includes several developers that have not worked with me, or with MVP. As such, I'm seeing these developers go through the very same questions, pain, trials and tribulations that I've gone through in the last year. Hopefully, I'm able to prevent much of this pain for them - when I see a person questioning or implementing in ways that I don't think are correct, I do what I can to provide additional guidance, information on my experience with what they are doing and general direction for moving forward. With this in mind, I can assume that there are a lot of developers out there asking the same questions and traveling down the same paths. My hope is that I can provide some answers and clarifications to help make the journey easier, not only for those that I work with, but also for those learning this on their own. Assumptions I'm assuming that you are familiar with several key concepts that MVP deals with, including: I'm assuming that you have some knowledge of and experience writing MVP code, and that you are searching for answers to questions in how you should implement what, where. I'm assuming that you are doing an MVP setup because you want testability in your code, aside from the other benefits that this separation of concerns gives you. Many of my comments and best practices assume that you are unit testing. And lastly, I'm implementing MVP with these best practices in C# and Visual Studio 2005 - my current language and environment of choice. These concepts, though, are (mostly) language and environment agnostic. You should be able to apply them to any .NET language, Java, maybe even Ruby / RoR (though it's likely that these concepts are built into RoR); and these concepts are not limited to web development. I am currently coding client-server windows applications, pocket pc applications and web applications, using MVP very successfully. 1) The Presenter is the Active Controller; The View is a Passive Doer Rule #1 - The view is a passive, ignorant doer. It has no knowledge of when to do anything. It has no control over anything. It does what it's told, when it's told to do so, by the presenter. The only thing the view knows, is *how* to do what the presenter tells it to do. This probably the most important concept to get right, when implementing an MVP setup - all other concepts, implementations and techniques that can be used, are mere side-effects and implementation details of this concept. And yes, you can guess the MVP camp that I live in, based on this statement. :) (Note: Don't confuse the term "active controller" with MVC, or the "supervising controller" MVP pattern. I'm using this term very loosely here, to help you see that the presenter knows what to do and when to do it; the view only knows how to do what it's told to do.) 2) Presenter: Speak to the IView Interface Never allow your presenter to speak directly to an implementation of a View, or to know anything about any specific implementation of a View. Breaking this rule will cause your code to be un-testable by introducing references and resource requirements that are outside the scope of Unit Testing. In fact, I will go so far as to say that the actual view implementation is completely, totally useless and irrelevant to the process that you are coding. If you find yourself in a situation where you can't code the process without having an implementation of the view, then you need to step back and re-define the boundaries between your view and presenter - see rule #1, and refactor. But, if the view implementation is completely useless, how do know that the process being coded will work? How do we ensure that the presenter talks to the view at the right times? The answer is: mock / stub objects in your unit tests. RhinoMocks is your friend. If you don't like RhinoMocks or any other mocking container, write manual stub objects. Just remember rule #1 when you code the mocks / stubs. If you break rule number one when you implement your mock / stub, then you need to re-think / refactor. Additionally, don't let your view's interface define anything more than how the presenter speaks to the view. This brings us to the next point: 3) View: Don't Speak Through the IView Interface*, Speak to the Presenter If you find yourself adding methods and properties to the view interface, and these methods and properties are never called by the presenter, then you are likely trying to define methods that the view uses to speak to the presenter. Typically, in the situation, you find yourself creating method signatures on the view interface, and then in the view implementation, the implementation for that method is empty - you're not actually using it for anything because the presenter is never calling it; or, you find that the implementation of the method does nothing more than make a call to the presenter. In either of these situations, you are likely trying to define how the view speaks to the interface, in the wrong location. What you should be doing, is creating public methods and/or properties on the Presenter so that the view implementation can call these methods and let the presenter know that something just happened. * The major exception to this rule is when you have made the design decision for the view to communicate with the presenter via events raised by the view. In this case, your view interface defines the event, the view implementation raises the event, and the presenter listens to the event through the view interface definition. 4) View to Presenter: What Should be Communicated, and How? Riding on the tails of item #3 is an inferred rule: what the view communicates to the presenter, and how. The "what" is significantly more important than the "how". There are dozens, if not hundreds or thousands of ways to implement the "how" - everything from events defined in the view interface, to public methods on the presenter, to abstractions and auto-magic wiring of messages and method calls. The "how" largely depends on the MVP (or MVC) framework that you are using. I am going to assume that you are not using an existing MVP framework and that you are coding up the view and presenter by hand, allowing them to shake hands and talk, as needed. So what should the view communicate to the presenter? Event information. Let's take a screen with a button on it, for example. When a user clicks that button, the button raises an event that is handled by the view's implementation. Once the view's implementation is handling this event, what do we do? If you look back at rule #1, the answer should be obvious - the view shouldn't do anything other than communicate this event to the presenter, so that the presenter can decide what to do. If the presenter needs the view to do something at this point, the presenter will tell the view what to do, and when. Let's extend this to a slightly more complicated situation - let's say there is a text box on the screen, and a user of the system is allowed to change the text in that text box. What should the view do when a user changes the text in this box? The answer hasn't changed - the view should communicate this event to the presenter. The only difference is that the view is now communicating the event occurrence with the data associated with the event - i.e. the view tells the presenter that the text was changed, and here is the new text. 5) Presenter: Don't Trust the View - It's Implementation is Unknown What is the view, in our Model-View-Presenter code? It's an interface - a definition of the methods, properties, etc. that need to be implemented so that the presenter can properly communicate with the view implementation. Step back for a minute, and think about how you code the presenter - you code against an interface definition, not against an actual implementation. If you are coding a presenter, and your presenter only knows about the view interface - then how do you know what the actual view implementation is going to do? You don't know, plain and simple. Unfortunately, in most software development projects, a developer is given an entire M-V-P stack to code. The developer creating the presenter is likely to be the developer that is implementing the actual UI for the real view. This situation lends itself to bad coding practices - it's easy for the developer to make assumptions about how the view will behave, when coding the presenter; and letting the view make assumptions about how the presenter behaves, when coding the view implementation. Be honest with yourself - when was the last time you made an assumption about one class, while coding against it in another class? I'm guessing it was 30 minutes ago, or less. If not, then I commend you on your discipline - seriously, it's hard not to do that. But, why are these assumptions dangerous? Why shouldn't we "know" that the view will handle data this way, at this time, when we code the presenter; or "know" that the presenter will call these view methods at this time? Let's look at this from the "allow assumptions" methodology, first. Code a presenter that works perfectly and processes all of the data in the correct sequence; but put all of the data storage in the view's interface. Don't let the presenter hold onto any data - only let the presenter read data from the model and read data from the view, when the presenter needs data. If the presenter needs to hold onto data, push it out to the view and read it back in from the view when it's needed. What's the end-result of this situation? Assuming you are the person implementing both the presenter and the view, you are probably safe and things probably work great. Now - what happens when Joe Frankfurter, fresh out of college, gets assigned to your project and has to change the UI elements on your view? Chances are, Joe is going to forget to hold the data properly, accidentally overwrite the values and references, and generally cause the data stored by the view to be invalid and/or corrupted. How does your presenter work, now? It's either throwing exceptions left and right; or if you've coded perfect exception handling and null reference checking, it's at least popping up warning signs all over the place, saying that it can't find the data it needs and/or asking the user to re-enter information they have already entered. The worst part; Because of the complex requirements and extraneous properties and methods on the view, Joe spent 2 weeks working on that view and you are delivering the project tomorrow... guess who gets to work all night, to fix the bugs? Now let's look at this from the "don't assume anything" way. Code a presenter that works perfectly and processes all of the data in the correct sequence; and code the presenter so that it maintains private references to all of the data that is needed for these processes. Ensure that the presenter is in control of the real data that is being processed, 100% of the time. When the view tells the presenter that some new data is available, the presenter validates, verifies, and quadruple-checks the new data to ensure that it really is valid and to ensure that it will not cause issues with processing. If the presenter finds that the data is not valid, it tells the view to prompt the user with a message stating why the data is not valid. Now let's have Tom Cheeseburger join the team, fresh out of college. Put Tom on the view implementation for this new presenter and let him make all the same junior mistakes that Joe was making. What's the end result, this time? When Tom drops the object reference, corrupts the data, and overrides data with invalid values, the presenter hums along nicely and the process still succeeds. At the very worst, when Tom has the view tell the presenter some bad data, the presenter says "I don't think so" and tells the view / user why that data is bad. The end result, though, is that the presenter - having complete control over the data that it needs to process - is very stable and the process is more likely to succeed, even when the view implementation is terrible. The worst part; because of the simplified view interface with only a few properties and method, Tom spent 2 days coding this view full of bugs and you only have a week and a half until you have to deliver it. Oh, wait - that doesn't sound so bad when compared to Joe's situation. A little guidance for Tom, few informal code reviews, and Tom's view implementation is good to go with plenty of time to spare. Which of these two situations would you rather be in? Ask yourself how many time's you been in the Joe Frankfurter situation. I'm betting it's more than you can count or remember. Now, ask yourself how many times you've been in the Tom Cheeseburger situation? Chances are, if you are not coding with good MVP standards - not very often. Real World Example Before you blow off my examples (re: #5) as fictional, academic, or whatever - here's a true situation that illustrates the points I'm trying to make. I was leading a project last fall with a set delivery date. One week before that delivery date, the customer arrives and wants a demo of the product. The demo goes as expected - a few minor things here and there.. and then disaster strikes. The customer asks "so... what about requirement XYZ from the User Requirements Document?". After a few small heart attacks, I go back to my developers - who literally were fresh out of college... this is their first job, this was their first major project - and asked them to start work on the requirement with the understanding that they would need to work extra hours, etc. to get it done. Guess how long it took? ... Less than 24 hours. Two developers, fresh out of college, implemented an entire vertical slice of an application - including the enterprise integration and messaging slice - in less than 24 hours. How's that for proof in the pudding? Summary Take a moment and look back at the five best practices that I've listed out, and notice how the first one is very short, very terse, and very high level... then look at number five again - it's very long, very verbose, and very low level (in comparison). I've organized these best practices in this manner with a specific goal - start with a high level goal and provide supporting concepts and guidance and each level below. If I when any deeper, at this point, I would be writing code instead of providing concepts and guidance (and the code to support these concepts deserves and entire post of it's own). Hopefully I've helped answer some of your questions about MVP. If nothing else, I hope that I've at least provided some guidance that will help you to answer the questions for yourself. One last important note: These "best practices" are entirely subjective, based on my research, experience, and actual usage of MVP in the last year. Everything in this post is subject to change without notice, when I find a better way to do things. ... In other words, just because I said so, doesn't mean it's right. :) Please keep that in mind as you move forward with your MVP usage.
|
-
At the company I work for, we have a CruiseControl.NET server to run our Continous Integration builds. Our typical project CI process includes the following steps: - Check for changes in Subversion repository and pull down changes
- Build the project
- Run unit tests
- If all unit tests pass, deploy to "Dev" server, for developer testing
As part of our standard web project process and environment, we have the following web sites in place for each project: - Developer's machine (where the developer writes code)
- Development Test (the CI end-point)
- Test Lab (for internal QA cycles)
- UAT (end-user acceptance testing, if we host it)
- Production (if we host it)
I think this is a fairly common setup for web development projects, in Agile-type shops. We do some agile process and development practices, and we're constantly adding to them. Overall, I'm happy with our environment and processes. Here the problem, though: Our company has grown rapidly in the last 2 years, and we now have around 10 different web projects that are either in active development or as-needed-maintenance cycles. With so many web projects in place, the process of promoting code from our Dev server out to the Test lab, and then out to UAT and Production, has become a nightmare. Until now, we have done remote desktop sessions onto the build server, and manually run the builds against a Tag in subversion, for the deployment to Test/UAT/Production. This is not a viable solution, anymore. We have too many projects, with too many developers, and a too many people would need to log into the build server and run builds manually. The question is: How do we promote a build from one environment, to the next, automatically and on-demand? What are common solutions for automatic build promotions? We want to set up our environment so that we can automatically push a build from the dev site, up to Test, up to UAT, and up to Production, as needed. We want to be able to click a button and have the process to do the promotion kick off immediately, pushing from the correct location to the correct location. Any links to articles, any advice and general direction, any specific tools that you can recommend... any help at all... we've been trying to find a working solution for this, for a while now, and have come up with nothing. We are not afraid of change or spending $ when needed... if we need to move away from CruiseControl, if we need to buy hardware and software... we need a solution that works. We need it to work for .NET development, specifically. Our current ASP.NET projects are in .NET 2 and we are planning to move to .NET 3.5 as new projects come along or existing projects need big work.
|
-
Chad Myers over at LosTechies, posted some interesting questions about TDD. I don't consider myself to be a TDD master - actually, I've had the same questions that Chad has, over the last year - and I do have some opinions, now, and thought the world might want to know. :) I'm repeating Chad's questions here, and numbering them so I can respond accordingly. TDD Practice Questions - How much up-front design do you tolerate? How do you know when to stop (i.e. 'when algorithms start getting discussed, it's testing time')?
- What about certain "I just know we'll need this" type stuff (i.e. putting a try/catch{Console.WriteLine(ex);} in your console main() method?)
- When writing a test, in order to even get it to compile, you have to build an interface or two, a concrete class with some NotImplementedExceptions in it, etc. How far do you allow that to go?
- If, during the middle of testing on a story, you realize that your up-front design wasn't correct, do you stop and discuss with your pair right then, do what you need to do and proceed, or do you pull back and go back to full pre-test design mode on that story?
- When proceeding to a new story, you discover that a test you wrote for a previous story no longer reflects the requirements. Do you refactor that test immediately, mark it as ignored until you finish the current story and cycle back to the ignored one? Something else?
- If the new story's requirements involve a slight tweaking to an existing test, do you tweak it, or make a new one and discard the old one (i.e. 'No changing existing tests!' or 'Only change if it's a minor compiler issue, but otherwise don't change it')?
- If you're, say, building up your model and it passes tests, but you're seeing that it's infantile and that the next few upcoming stories will produce significant changes to produce a new emergent model, is it appropriate to step back and consider a larger refactoring, or do you keep plugging and make the changes into the existing model even if it could benefit from some housekeeping that is otherwise out of scope?
My Answers - Enough to know what I'm supposed to build.
You really need to answer this question based on your individual project. I recently built an enterprise integration project that took our core maintenance system and brought some disconnected operations into it. On this project, I did not have a lot of up front design. I knew that the system needed to operate in a disconnected mode, I knew it needed to be operational even when there was no one logged into the computer or running the software, and I knew that it needed to do bi-directional communication with the master maintenance system, to perform a limited set of maintenance related tasks. I started with a few assumptions / up front design - build a Windows Service to run the core process, build a client-server architecture to host the UI, build on top of a messaging system to support the communication needs. Beyond this, the functional requirements of those tasks drove the design of the project during the construction of the software. Conversely, I've worked with a team that was building a system for a laboratory, recently. With the requirements from the customer, the process that they requested, and the required conversion of existing data that the customer had; we required a large up-front design, to determine if the data conversion and functional requirements were going to be met by our proposed solution. The major design of the system was known up front, but the implementation was still a work-in-progress during the construction of the software.
- This is one of the questions that I still battle with, and I answer it differently every time I ask the question. I try not to add features and functionality unless I know that I need them right now - when it comes to the domain model and core infrastructure services of my application. However, with items like the Console.WriteLine(ex) question, I don't see any issue with adding those into the application's Main method. After all - you are not going to unit test the App's Main Method... you are going to integration test it.
- In terms of implementing interfaces, I try not to manually code stub objects anymore. Instead, I try to use RhinoMocks whenever possible so that I can avoid that question. I used to code stub objects all the time, and would have dozens of Not Implemented methods - the end result is that my unit tests were horrendously difficult to maintain and I had dozens of duplicate stub objects to provide a certain method to a certain unit test. RhinoMocks helped me clean that up and keep my unit test code stub object requirements manageable size. There are times when you really do need a stub, and when you do need one, you build the methods you need and allow the Not Implemented methods when you really don't need that method for your tests.
If you're asking about real objects that you are stubbing out the method signatures for, in your unit tests - you shouldn't allow NotImplemented methods to live in your code for very long... you should only be adding the methods that you need, for your software to perform it's required functionality. If you have a real object with a Not Implemented exception being thrown, you are probably looking at a method that can / should be deleted from the code base.
- Once again, it comes down to your specific situation.
I spent some time doing pair programming with a coworker, a while back. During this time, we continuously ran into situations where the next functional requirement caused a change in our existing designs. When this occurred, we would only make changes as we needed to. We would write a new unit test for some new functionality, and if the previous designs did not allow us to make the unit test pass, we would go back and modify our previous designs - making changes and updates to the unit tests in those changing areas, as we go. The great thing about unit tests in this situation is that you can consider them to be a "to do list" of sorts. During the times that we were changing our existing designs, we would often forget about an area or the new functional requirements that we were trying to satisfy. Fortunately, a complete run of the entire unit test suite would show the now broken tests and be a reminder to us on what we needed to change. During this pair programming time, there were a few occasions where we got to a new requirement and looked back at our design and implementation with the realization that the entire model was wrong and needed to be re-written from the ground up, with the knowledge of this specific requirement in mind. At those times, we did what a lot of people would never even consider - we deleted every class and every unit test in our system and started from scratch. The lessons learned along the way and the knowledge of other requirements lead us to a new design that accounted for the functionality that we would need - but we were still able to code the existing functionality on an as-needed basis. We provided extensibility through additional interfaces and abstractions, without creating "future-code"... stuff that "we know we'll need this in a bit, so let's do it now". I've gone through these "design, fix, redesign, fix, rebuild from scratch, fix" cycles numerous times. Every time it happens, it scares the heck out of me to delete existing code and start over. In my experience, though, the end result is worth the effort - fewer bugs in the system, a model that more accurately represents the business needs, and better extensibility and flexibility that allows for change in the future.
- Fix it as early as possible. If you don't fix it now, you're likely to create more work for yourself because you'll have something new that interacts with that broken piece, and will have to fix the broken piece and the new piece. If you fix the broken piece now, you won't have to re-write the new piece later.
- If you are changing the meaning of a test (i.e., you have to rename the test to accurately reflect what is being tested and why), then you are automatically destroying the old test and creating a new one - even if you are only destroying it semantically and you are really just renaming and changing a few details. If the meaning of the test remains the same, and you are only changing some of the implementation details (because you changed the design, via question #4 or whatever), then leave the test where it is and just change the implementation to meet the new implementation needs.
- See my answer to question #4.
TDD Style Questions - Assert.That(x, Is.EqualTo(y)) or Assert.AreEqual(y,x)?
- How many asserts/test? Any caveats?
- How do you know what to put in the SetUp method?
My Answers - Syntax is a personal choice and almost irrelevant. If they are both technically correct, then choose the one that most correctly represents what you are trying to say. I prefer to code in english as much as possible (read the first 9 chapters in Steve McConnell's "Code Complete 2", if you haven't). However, I also prefer simple APIs. I don't like the Assert.That(x, Is.EqualTo(y)) syntax because it creates a complex API set that I have to know. RhinoMocks is a great example of what you are suggesting, here. I love RhinoMocks and will continue to use it... but I don't always like the strange API calls that I have to make.
- If you have a single object and a single method call that is being tested, with a single output value; the answer is easy - you have one output value, so you need one assert. In the real world though, your have a system under test that interacts with two or more sub-objects and interfaces, each with multiple behaviors and / or values changes that get invoked when you make a single method call on your system under test. The real answer is that you need one assert for every behavior / value that you expect when that one method is called. If your method returns seven values and call two methods on a child object via an interface, you need nine asserts for that one test.
- Anything that is used by EVERY test in your test fixture. If there is even one test in your test fixture that does not need the information provided by the Setup method, then you should not have that information in the Setup test - move it to a common method that can be called by each of the tests that does need the information.
TDD Mechanics Questions - Is a refactoring tool absolutely necessary?
- If so, what is the bare minimum features that the tool would need to facilitate decent TDD?
- Integrated IDE test runner or a background source-watching auto-runner?
My Answers - Absolutely, 100% NOT required. In fact, I would recommend that you exercise your mind now and then, and purposely work without a refactoring tool from time to time. Remind yourself what it really takes to refactor something - understand the true cost of refactoring by doing it manually. When you understand this cost, you'll see why you really do want a refactoring tool. Personally, I can't stand coding without Resharper. However, it doesn't support Visual Studio 2008, at the moment, so I'm stuck refactoring everything by hand when I code in VS2008. It really makes me appreciate Resharper that much more and I'm looking forward to the EAP / release of Resharper 4.
- Resharper has a built in unit test runner, which is really nice. I use it whenever I'm in VS2005 and writing unit tests. However, TestDriven.Net also has some really nice integration into VS2005/2008 for unit test running. I use this in VS2008, since I can't use Resharper at the moment.
- Both. See #2, and build yourself a Continous Integration Server (We use CruiseControl (the CC.NET flavor)) with automated unit test running. We schedule builds on our CCNet server to run every 15 minutes, and every time it builds, it runs all of our unit tests for us.
Hopefully this information will be useful to someone else out there, who may be asking the same questions. I'm glad to see Chad asking these questions, and I know that I've asked the same questions in the past. The learning curve on this stuff can be very steep and very painful. If we all pitch in and try to answer these questions when they pop up, hopefully we can reduce the learning curve and entry cost.
|
-
apparently my current blog software strips out the embedded tags... so let's try not embedding them. private PropertyRulesValidationService<TeamMember> GetValidationRules()
{
PropertyRulesValidationService<TeamMember> validationService = new PropertyRulesValidationService<TeamMember>();
RuleValidationDelegate<TeamMember> validateFirstName = new RuleValidationDelegate<TeamMember>
(teamMember => { return !string.IsNullOrEmpty(teamMember.FirstName); });
validationService.AddRule("Require First Name", "First Name is required for a Team Member.", validateFirstName, "FirstName");
RuleValidationDelegate<TeamMember> validateLastName = new RuleValidationDelegate<TeamMember>
(teamMember => { return !string.IsNullOrEmpty(teamMember.LastName); });
validationService.AddRule("Require Last Name", "Last Name is required for a Team Member.", validateLastName, "LastName");
return ValidationService;
}
d'oh... it formats it... without color.
|
|
|