In this part we’re looking at the last verb of the blog entity, Publish. In part 2 I said that this was a special verb, and that in the real world the specification should explain these kinds of verbs.
I showed you this example.
A blog post will remain invisible to the public until published. When a blog post is published it should post to selected social media outlets, including the RSS feed.
Another way that this might be specified is through a user story.
As an administrator, I want to select a previously created blog post and mark it as published so that anonymous users can read the blog post. Functional requirements:
- The blog post must be marked as published.
- The blog post’s publish date must be set. The blog post should be shared on social media outlets
- Publishing of the blog post must happen in less than 2 seconds
I don’t have a real requirement document for my own website that we’re building here, I’m making this up as I go along. I had forgotten about the piece in part 2 when I wrote down the user story for this verb here in part 5. And as it often happens in the real world, the two pieces of text reflect essentially the same requirements, but the first one is just very poorly worded and leaves a lot of information implied instead of being specific. During your career you deal with different people, some of whom will be more competent than others. And so it is not uncommon to have to do real work based on very poor requirement documents. You’ll have to learn to read between the lines and across different parts of the specification to form a complete picture. And you’ll have to learn to ask questions. There is never anything wrong with asking questions.
A plan to design
So far we’ve taken the requirements directly to the unit tests, and let a lot of the implementation details emerge to us as we tested and coded. This is fine for small projects and one- or two-man development teams. However I want to convey a more professional approach from which you will get a lot more mileage in bigger teams and projects. And that approach is to plan before you start testing and coding.
You’ve heard the term before. Perhaps you’ve also heard about extreme programming. And scrum. These all revolve around two basic concepts - you commit to some work upfront, and that work is completed before any further work (or changes to the work) is considered. To be able to do this, for you to be able to commit to some work, you have to understand what that work will entail. Typically you have to size the amount of work or effort involved to complete it. And typically you have to break the work down into smaller pieces that you can fit into the “heartbeat” of your agile workflow. Sometimes that’s a two-week period, sometimes a three-week period. Regardless, you will have to plan your work in advance in order to successfully deliver on your commitment (or risk being under- or over-committed). In the workplace you do this during what the scrum-masters call "sprint planning".
Break it down
Planning for a piece of work obviously requires that you understand all the bits that you have to do in order to complete it. We will need a list of things to understand. And as before, we start with the list of things we need to test for. Always start at the tests.
- Test that a blog is marked as published.
- Test that a blog’s published date has been set.
- Test that we push to each social media outlet.
Well, that was simply repeating the functional requirements, wasn’t it. Perhaps we can do a bit better than that.
- Test that a boolean property is set to true on a blog entity to mark it as published.
- Property: IsPublished
- Unit Test: BlogServiceTests.Publish_IsPublished_MarkedTrue
- Test that the publish date is set to today’s date when the date wasn’t set by the user.
- Property: PublishDate
- Unit Test: BlogServiceTests.Publish_NoDate_IsSet
- Unit Test: BlogServiceTest.Publish_DateSet_IsNotSet
- Test that we push to each social media outlet when publishing a blog
- Iterate through a list of `IBlogSyndication` adaptors and call into each one
- This process might take longer than 2 seconds though
- Will need an out-of-process long-running task
- Perhaps look at IHostedService
- Special processing abstraction that we can mock.
- ISyndicationProcessor, async Task Syndicate(string url, DateTime date, string description)
- Find out what the common elements between all the different social media platforms are. RSS has at least url, date, description and title.
- IBlogSyndication.Push(string url, string date, string title, string description)
- Unit Test: BlogServiceTests.Publish_Verify_LaunchHostedServiceProcessing
- Unit Test: SyndicationProcessorTests.Syndicate_LoadSyndicationConfig
- Unit Test: SyndicationProcessorTests.Syndicate_Verify_SyndicationAdaptors
- BlogService method: async Task Publish(string title, DateTime? publishDate)
That looks a lot more detailed, and there is a lot to cover. Where did all this information come from? It came from thinking about and discussing the user story with your fellow team members. It came from actively designing the processes upfront so that it will fulfill the requirements. It came from discussing the test cases with the QA people during the planning session.
On a bigger project the design of the solution is always a team effort. This is because an individual’s logical thinking and common sense is not infallible and they will miss something. And also because everyone in the team needs to understand the requirement and the implementation of the solution.
I’ve revised that list above probably 5 times now and spent at least three hours on it. There isn’t a real team involved here. It’s just me so it remains to be seen how much foresight I really could apply here. Planning is really hard, but it is also really crucial. Don’t skimp on it. I paraphrase from an old Top Gear episode: if you fail to plan, then you should plan to fail (who can tell me from which episode that quote is from?).
The items on this list would now typically go onto some board. Perhaps on Trello, or TFS or Jirra. Maybe even a real, physical whiteboard with Post-It notes. The list constitutes tasks that need to be completed, and those tasks might be assigned to different people.Another output from planning is additional documentation artifacts that assist in understanding, and can sometimes be considered deliverables to the client as well. One such an example is sequence or flow diagrams (typically one per feature or story). In part 1 and 2 we saw the high level domain diagram and the first level solution diagram. The sequence diagram helps us to understand on a very detailed level what the steps are that we need to follow in order to complete the task.
This diagram shows us a few things. It shows us the orchestration that needs to happen when a blog is published. It shows the data that is being sent and will be returned. It shows us our integration points with our abstractions. It even shows us the API endpoint and the HTTP result code. It shows us that we will do a full read of the blog entry from the database and then update that entry again, and then before we return we will asynchronously call into the
IHostedService to process some stuff. You can go into a lot more detail here if you want, like showing one or two error sequences also.
Changes to previous designs
Those paying attention will have noticed that we’ve included in our plan some overlap with previous parts. We have the same PublishDate unit tests here as what we built in part 4 for the Create verb. This happens all the time, where you uncover functionality that overlap, or new requirements that will change what you did before. This is normal and should not raise any alarms or cause concern. Change to a software system is absolutely normal, even while it is still being built.
In this part we discussed how to plan and how important it is. This part has taken me tremendous effort to write. I have a small little whiteboard here next to my computer that I use, and I drew this stuff out over and over again, until I got something that I was finally happy with before duplicating it digitally for this blog. At my day job we typically dedicate an entire day to planning the work for the next sprint. It is hard and it is tiring. But if you do it well, with enough detail and documentation, as a team, you will reap the rewards over and over again. This plan that we now have will probably keep me busy over the next few parts, so you’ll have to bare with me through the coming unit testing and read/green/blue rinse and repeat.
Following the plan
Looking at the plan from part 5 gives us a nice path to follow to completion. We’ve already detailed exactly where we need to start (the tests!). So, without further ado...
In this part, we start to look at the final part of the Publish verb - pushing content to different social media outlets. This will be the hardest task we’ve done so far, and it requires some upfront research first. Understanding the problem that you need to solve is much more important than trying to design a solution (This is also true when you’re all about your startup idea). That makes logical sense of course, but I’ve often seen developers barely finish reading the user story's first line, and they are already furiously typing up code or drawing database and architecture diagrams, believing they ‘see the big picture’. And sometimes that might be true. Perhaps they’ve solved a similar problem before or they have lots of experience in a domain industry like fintech. But neglecting the details and the exceptions (stuff like edge cases for example) will always result in rework. And often that rework takes the form of what I like to call “programming through permutations”. This is when the code almost works but the developer is not sure why it breaks on a particular use case or edge case. And what you’ll often see is that they will make a small change and test, make another small change and test again, rinse and repeat, until it works. Often this can take up an entire afternoon, simply moving bits of code around or changing greater-than comparisons to greater-than-equal ones. And when it finally works they still have no idea why it broke or how their final change caused it to work.