By Martelle Esposito, Eleanor Davis (Code for America), and Sasha Reid
These toolkits were developed with the WIC Technology Resource Group, a resource and learning partnership between the National WIC Association and Nava, with contributions from Code for America. Many thanks to Mandy Brown and Genevieve Gaudet for their invaluable contributions.
Technology can be used to achieve meaningful program outcomes when you focus on what people need, approach projects one manageable part at a time, and measure outcomes.
This toolkit is the second of four that will walk you through human-centered and agile processes to help you achieve meaningful program outcomes with every new technology project. We recommend using them in this order: Setting goals, Building technology, Engaging users, and Measuring success.
This toolkit can help you:
Use technology to achieve meaningful program outcomes
Center technology development around what people really need
Once you’re clear on your goals and outcomes of your technology project, you have to build the thing. In this toolkit, we’ll share some guiding principles for managing a human-centered technology build: using a modular approach and setting your project up for success.
Human-centered design is:
a methodology for designing and building unified, clear, and respectful products and services that incorporates feedback from the people you are designing for throughout the design process. Its core principles are to build an explicit understanding of users, involve them throughout the development process, test and iterate frequently, and address the whole user experience of a product or service.
Agile development is:
a collection of project management and software development practices that shorten feedback loops and rely on constant feedback, iteration, and collaboration to improve products and help teams quickly, safely, and cost-effectively build software that meets people’s needs.
Using a modular approach to software development
Modularity is the principle of designing and building a complex system out of smaller parts. It does not define how small those parts must be, or how complex the system has to be before it “should” be a modular one. That’s up to you.
You can think of LEGO pieces as modules. They come in a variety of sizes and colors and can be assembled to build pretty much anything. You can also take apart what you’ve built to create something new or just replace specific pieces. Like LEGO pieces, modules can be assembled, disassembled, replaced, and reused.
Modular vs. monolithic approaches
The opposite of a modular approach is monolith. A monolith is one single application that contains all of the complexities and business logic (processes like “creating a new WIC applicant”, or “recording a nutrition check-in”). This means that changes to one part of the code can impact the entire application, and replacing functional modules within a monolith is very high risk.
There are some benefits to monolithic software. For example, a monolithic application build likely has fewer dependencies on software outside of your control. And, when building a new software solution, it is inherently faster to build a monolith. But monoliths have their drawbacks as well. They’re less flexible, scalable, and changes can be difficult and high-risk because they impact different parts of the code.
Benefits of a modular approach
Why would you build a modular system? Here are some benefits of approaching systems this way.
Replaceability: Smaller parts are easier to remove and replace. This is the biggest driver for a lot of teams. Having individual parts means it’s easier to decide to replace or outsource one module either with a new, improved version or a third-party solution.
Parallelization: Independent modules can be worked on by multiple teams. That makes it easier to spread work on a modular system across development teams.
Maintainability: Each module is smaller and easier to change. It’s easier for new teammates to understand a module, and it’s easier to make changes without unexpected side effects within that module compared to one application that incorporates all functionalities.
Composability: Ability to move modules around, change which modules talk to each other, or how many copies of the “Login Module” are running (for example). Teams can disassemble the modules and reassemble them in a totally different order or quantity. Having a box full of reusable modules that all “fit together,” or that can talk to one another, means that if you see an opportunity to change or improve a component by having them talk to each other in a different order, you can.
How to build a modular software system
Now that we’ve established the benefits of a modular system, let’s talk about how you can actually build them.
There are two basic approaches to building modular software:
Design your modules separately from the beginning. Each module gets its own area of responsibility, often based on the business context it is responsible for.
Build a monolith and then decompose it into modules. After building a single monolithic system, the team separates that monolith out into modules.
With either approach, the modules themselves are typically either a discrete part of a program, like “scheduling,” “intake,” or “benefits,” or a logical grouping of functions, like “user management” or “nutrition data.”
How modules interact
There are multiple ways that modules can interact with one another, and each offers its own unique tradeoffs. This is not an exhaustive list of mechanisms that a modular system could use, but here are the basics.
Event system: This is a general term to describe a family of potential solutions. These can involve passing messages through brokering software or a system where it is possible to publish and subscribe to messages. Think of a mailing list: you can sign yourself up to receive the latest newsletter, and that’s the equivalent of “subscribing”. When someone sends the latest update to that mailing list, that is the equivalent of “publishing”. While the language that modules would use to do this is different, the concept is the same. Examples of event systems include an enterprise bus, an asynchronous queue, or a “pub/sub” solution.
Remote Procedure Calls (RPC): One module connects to another and asks it to directly run a procedure — similar to calling an automated telephone number to pay your cellular phone bill. The phone call you’re making is the remote procedure call, and the payment is the procedure you’re making happen through that phone call.
Identifying how many modules you need
Another important decision point is figuring out how many modules you need. The best approach is as few as possible, but no more.
Too many modules adds burden and complexity to your system and each module might not have sufficient functionality to make it coherent and add value to the system. But if you have too few modules, you might end up with a module that’s responsible for several unrelated functions.
One way to determine how many is the right number for you is to use domain driven design. While you could take a course on this approach, the basic idea comprises two steps:
Map out the problem space or the set of user needs and pain points you’re trying to solve for.
Align the modules with those areas by grouping aligned parts of that problem space together, creating boundaries between each group.
The end goal is to have your mental model of a system aligned with the map of modules and how they interact with one another. Let’s walk through an example of how this works.
Example: Building a comments section using modularity
Let’s go through building a system that lets people post comments on a webpage.
A list of basic functionality for a comment system might include:
post a comment
edit a comment
vote (up/down) on comments
Building a monolith and decomposing it into modules
If you were to take the “decomposition” approach — meaning that you start with a monolith and break it down into modules — you might start with the basic functionality of the comment system and put it all into one system called Comment System. That system would do everything: user logins, profile updates, voting, and commenting.
After that, you might break down that monolith into 4 modules:
Account management to handle user logins and password changes
Profile management to handle reading or updating a profile
Comments to handle posting a comment on a webpage, editing that comment, or reading the list of comments
Votes to handle upvoting and downvoting a comment.
This is just an example. You could decompose that monolith into smaller modules, or, as another example, you could keep “account” and “profile” together in one module.
This approach is often faster to start developing than creating modules from the beginning, but likely involves more work the longer you put off the decomposition process. All of the interactions between modules need to be sorted out, and the deployment process for installing the modules on servers and testing need to be addressed. The more the software grows, the harder it becomes to disentangle the intrinsic parts from the whole.
Designing modules from the beginning
If you instead took a modular approach from the beginning, you could start with each module having a single function. The “Account Management” module would start with the ability to login, the “Profile Management” module would start with the ability to read a profile, and so on.
Then as you continue development on these modules, you add additional complexity. You might add “changing your password” to the account management module, for instance. This allows you to pay off the upfront cost of building multiple modules early and solve the complexity of the modules interacting with one another before adding more functionality to those modules.
Try it: Break a project down into modules
Imagine how you might tackle an application for a WIC program. Here are some things you might consider:
What parts of WIC share data with one another?
What parts of the program belong together and how are they connected?
What are some modules you would recommend for WIC enrollment and certification? For other parts of WIC service delivery? Why?
Try just drawing boxes on a piece of paper, one for each grouping you come up with. Then, you can connect the boxes together with lines where they talk to each other, so if one box contains “Application,” and one box contains “Participant Details,” there might be a line between those to represent how an application is associated with the participant who applied.
Setting your project up for success
There are many detailed resources available with details for how to structure your technology development team, plan your project roadmap, and procure technology services in a way that supports the human-centered technology development principles. This is an abbreviated guide.
Structuring your technology development team
Your technology development team should be a true partnership between agency and vendor. Agency staff should be active players in agile development.
To accomplish this, agencies should consider hiring or training staff to play an active role in product development as full-time or near full-time product owners. Product owners are the project leaders who make decisions and prioritize work based on ongoing user research and testing and engagement with the vendor. It’s critical for the agency product owners to be empowered to make decisions and able to spend the time to stay up to speed on all the latest user research and testing.
Planning a project roadmap
When creating a project roadmap, plan for cycles of discovery, testing and implementation. A modern, human-centered approach to technology involves multiple cycles of discovery, user research, prototype testing and implementation — not just one planning phase followed by one implementation phase. Project roadmaps and other planning documents should account for this to give you the opportunity to be iterative and create final products that meet user needs.
Procuring technology services
When procuring technology services:
Start with your agency and user needs (not functional requirements)
Engage the vendor community
Build modularly and incrementally
Ask vendors to show not tell
You can create a user-centered procurement as opposed to a requirements-driven one by defining user and agency needs rather than technical requirements. Ask vendors for solutions to meeting those user needs. That means really engaging the vendor community: share your user needs and let vendors respond through informal conversations or an RFI process. There are also many great resources to help with procurement, including the 18F Derisking Guide.
The Wisconsin Unemployment Request for Proposal (RFP) is a great representation of a very modern procurement, starting with a small scope project for one year, plus optional renewals. They can increase the scope later with the same vendor or with a different vendor for future modules. This approach also de-risks the larger project as there is more control to move between vendors or stay with the same vendor if they want.
Now you’re almost, but not quite, ready to start planning to build the technology. The next part of the process is engaging users. Engaging users before you build the technology, during the build, and during implementation is essential to ensuring you build the right thing to solve the right problem. Learn more in Engaging users and iterating.
About the WIC Technology Resource Group
The WIC Technology Resource Group is a resource and learning partnership between the National WIC Association and Nava, a government technology consultancy and public benefit corporation, and with contributions from Code for America, a nonprofit focused on digital tools for government.
Power your mission with human-centered, agile technology is a series of four toolkits that will walk you through human-centered and agile processes to help you achieve meaningful program outcomes with every new technology project. It includes: Setting goals, Building technology, Engaging users, and Measuring success.
For more resources, visit the WIC Hub.