'INVEST' Guidelines for Agile User Stories

I am a recent graduate at the beginning of my software development career. I enjoy documenting my learnings through my blogs
"INVEST" was invented by Bill Wake and is an acronym that spells out how best to write effective and valuable user stories in Agile development. Each word in "INVEST" is a set of criteria that needs to be met to enhance the composition of the user stories.
I wanted to use code examples so that I can illustrate that "INVEST" can be linked to the development of code and developers can benefit from these criteria.
A good user story will incorporate these 6 criteria:
Independent: a user story can be executed without reliance on any other user story, system or team.
Example: As a user, I want to be able to log in using my email and password.
[Route("api/[controller]")]
[ApiController]
public class AuthController : ControllerBase
{
private readonly List<User> registeredUsers = new List<User>
{
new User { Id = 1, Email = "test1@example.com", Password = "password123" },
new User { Id = 2, Email = "test2@example.com", Password = "Password321" }
};
[HttpPost("login")]
public IActionResult Login([FromBody] UserLoginRequest loginRequest)
{
// Check if the entered email and password match any registered user
var user = registeredUsers.FirstOrDefault(u => u.Email == loginRequest.Email && u.Password == loginRequest.Password);
if (user != null)
{
// Successful
return Ok(new { Message = "Login successful!", UserId = user.Id });
}
else
{
// Not successful
return Unauthorized(new { Message = "Invalid email or password. Please try again." });
}
}
}
This user story can be developed independently, it does not have a depency on anything else.
Negotiable: the user story is open to change, aspects of the story can be removed or adjusted to make the feature quicker to develop. The specific details can be refined at a later stage in development.
Example: As a user, I want to be able to filter search results by price.
As developers we need to be able to predict future changes 🔮, and make our code as easy to adjust as the user story.
List<Product> searchProducts(SearchProductsRequest request){
// code goes here
}
// called using
var request1 = new SearchProductsRequest() { Category = "clothes", MinPrice = 10M };
var request2 = new SearchProductsRequest() { Category = "clothes", MinPrice = 10M, MaxPrice = 34M };
var request3 = new SearchProductsRequest() { MinPrice = 10M };
searchProducts(request1);
searchProducts(request2);
searchProducts(request3);
// I am using an object because this allows for change within the code,
// and can be changed in one place without affecting any other usages of this object.
class SearchProductsRequest {
public string Category { get; set; }
public decimal MinPrice { get; set; }
public decimal MaxPrice { get; set; }
}
This user story can be altered and discussed by the team and stakeholders. The specific requirements (e.g. price range) can be added at a later stage.
Valuable: it is important that the user story adds value to the end user upon completion.
Example: As an online shopper, I want a 'Wishlist' feature, so that I can save items for a later purchase.
User user = new User { Id = 1, Username = "Mary" };
Product laptop = new Product { Id = 101, Name = "Laptop", Price = 999.99 };
Product headphones = new Product { Id = 102, Name = "Headphones", Price = 79.99 };
Product phone = new Product { Id = 103, Name = "Phone", Price = 499.99 };
// Add products to the wishlist
Wishlist wishlist = new Wishlist(user);
wishlist.AddToWishlist(laptop);
wishlist.AddToWishlist(headphones);
wishlist.AddToWishlist(phone);
public class User
{
public int Id { get; set; }
public string Username { get; set; }
}
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
public double Price { get; set; }
}
public class WishlistItem
{
public Product Product { get; set; }
public DateTime AddedAt { get; set; }
}
public class Wishlist
{
// code goes here
}
This elavates the users experience when shopping and provides a means to save items for future purchases, thus adding value to the end user.
Estimable: the team can understand the user story and can determine the effort and resources required to complete the feature.
Example: As a user, I want to receive notifications for all new emails recieved.
This story is clear for the team to understand and an estimation of effort can be determined.
Small: the user story can be completed in a small timeframe, usually 1-3 days.
Example: As a user, I want to see recommendations based on my browsing history.
<ul id="recommendationsList"></ul>
<script>
document.addEventListener('DOMContentLoaded', function () {
var browsingHistory = [
'Product A',
'Product B',
'Product C'
];
// Get the recommendations list element
var recommendationsList = document.getElementById('recommendationsList');
// Display recommendations based on browsing history
for (var i = 0; i < browsingHistory.length; i++) {
var recommendationItem = document.createElement('li');
recommendationItem.textContent = 'Recommended: ' + browsingHistory[i];
recommendationsList.appendChild(recommendationItem);
}
});
</script>
This user story is simple and concise and could be completed within a single sprint.
Testable: the user story can be clearly tested when completed to make sure it is in line with the acceptance criteria and works as intened.
Example: As a business, I want the 'Submit' button on the form to be disabled until all required fields are filled.
<form id="myForm">
<label for="firstName">First Name:</label>
<input type="text" id="firstName" required>
<label for="lastName">Last Name:</label>
<input type="text" id="lastName" required>
<button type="submit" id="submitButton" disabled>Submit</button>
</form>
<script>
document.addEventListener('DOMContentLoaded', function () {
// Get form and submit button
// Add event listeners to form elements
function checkFields() {
// Enable the submit button only if both textboxes are filled
var firstName = document.getElementById('firstName').value;
var lastName = document.getElementById('lastName').value;
submitButton.disabled = !(firstName !== '' && lastName !== '');
}
});
</script>
This user story has clear testing ability through the buttons behaviour, and tests can be automated or manually checked.
Following the 'INVEST' guidelines ensures that the user stories are unambigous, provide value and are attainable. It encourages collaboration between the team and offers a shared understanding of expectations.



