AI-Augmented Software Engineering - The Series in Practice
Part 8: First Coding Sprint to Final Cutoff Sprint
The rapid pace of agile software development demands efficient ways to produce high-quality code and deliver value to customers. Large language models powered by artificial intelligence can help streamline the coding and testing sprints in an agile framework. For example, they can generate documentation artifacts like user stories, test cases, and release notes using simple prompts. This automates tedious documentation tasks, freeing up developer time. They can also provide intelligent feedback on code quality, identify potential bugs, and suggest improvements - acting as automated code reviewers. For managers, they can analyze sprint progress and team velocity to provide data-driven insights into the development process. By integrating these AI capabilities into the agile workflow, teams can boost productivity, reduce errors, and bring innovations to market faster. The natural language powers of large language models look set to revolutionize how modern agile teams build and deliver software.
Coding Sprints
Here is a recap in two paragraphs using future tense:
During the agile sprints, the coding teams will handle tasks aligned to defined epics and user stories, participating in code reviews and pulling in the repository owner to oversee quality. The scrum master will ensure standards are met and the work board stays updated. In Sprint 1, engineers will implement Django models for to-do tasks and create supporting views and templates, with pull requests and reviews validating basic CRUD operations. Sprint 2 will build upon this foundation by enabling marking tasks as done and enhancing the UI. Testing will occur in tandem, with unit tests written for each new functionality.
Over future sprints, additional features like user accounts or deadlines could be added based on feedback. Each sprint will conclude with a review and retrospective. In final stages, comprehensive testing will occur, documentation will be updated, and deployment steps like gathering static files and database migrations will be handled. The agile process will produce working software through iterative coding, testing, and feedback loops.
Intelligent Feedback
We’ve mostly planned out our application and can only assume that, as in normal development, issues may arise. What exactly those are can’t be predicted however we can assume there would be some debugging. In that case a pattern such as Few Shot or Chain of Thought may be helpful with ChatGPT-4 to debug code issues as they arise.
Intelligent Feedback
For the base assets generated by Django and python we will run through a series of prerequisites steps. This is where expert-level knowledge comes in, however if we didn’t know how to do this we could ask ChatGPT or Bing using the PQD pattern:
Our input prompt:
You are an expert python programmer and are teaching a tutorial on python. You are working on a Django web application with a Postgres backend, as well as git and virtualenv.
Show me the steps to create a github repo, clone it, create and start a virtualenv inside the base repo, create and start a basic Django project called “todo_project”, as well as set up a postgres database backend for it.
Comment each step of the process thoroughly. All of the instructions should be tailored for a Debian based Linux system Provide a few different solutions and explain the rational behind each.
(NOTE: you don’t have to use a Debian based Linux system, that’s just what I’m using to write this article)
Output response:
Certainly! Let's go through the steps:
### 1. Create a GitHub Repository
1. Visit [GitHub](https://github.com/).
2. Login with your credentials.
3. Click on the '+' icon on the top right corner and choose 'New repository'.
4. Name your repository (e.g., "django-todo-app") and choose other settings based on your preference.
5. Click 'Create repository'.
### 2. Clone the Repository
On your Debian based system, use the following commands:
```bash
# Navigate to the directory where you want to place your project
cd path/to/directory
# Clone the GitHub repository
git clone https://github.com/your-username/django-todo-app.git
cd django-todo-app
```
### 3. Set Up a Virtual Environment
There are a few methods to set up virtual environments in Python. Here are two common ones:
#### Using `venv` (bundled with Python):
```bash
# Install venv if not installed
sudo apt-get install python3-venv
# Create a new virtual environment
python3 -m venv venv_name
# Activate the virtual environment
source venv_name/bin/activate
```
#### Using `virtualenv`:
```bash
# Install virtualenv
pip install virtualenv
# Create a new virtual environment
virtualenv venv_name
# Activate the virtual environment
source venv_name/bin/activate
```
**Rationale**: `venv` comes bundled with Python3. However, `virtualenv` can offer more functionality and can create environments for arbitrarily installed python versions.
### 4. Start a Django Projec
First, install Django:
```bash
pip install django
```
Now, create the Django project:
```bash
django-admin startproject todo_project .
```
### 5. Set Up Postgres Backend
#### Install PostgreSQL and psycopg2
```bash
# Install PostgreSQL
sudo apt-get install postgresql postgresql-contrib
# Install psycopg2 (Python adapter for PostgreSQL)
pip install psycopg2-binary
```
#### Set Up a New PostgreSQL Database and User
```bash
# Access PostgreSQL command line as superuser
sudo -u postgres psql
# Create database
CREATE DATABASE tododb;
# Create user with password
CREATE USER todouser WITH PASSWORD 'password_here';
# Set default encoding, tsearch2 (for full-text search) and timezone
ALTER ROLE todouser SET client_encoding TO 'utf8';
ALTER ROLE todouser SET default_transaction_isolation TO 'read committed';
ALTER ROLE todouser SET timezone TO 'UTC';
# Grant all privileges on the database to the user
GRANT ALL PRIVILEGES ON DATABASE tododb TO todouser;
# Exit PostgreSQL command line
\q
```
#### Update Django Settings for PostgreSQL Backend
Navigate to `todo_project/settings.py` and make the following changes:
```python
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql',
'NAME': 'tododb',
'USER': 'todouser',
'PASSWORD': 'password_here',
'HOST': 'localhost',
'PORT': '5432',
}
}
```
### Final Thoughts
Remember to make regular commits to your GitHub repository:
```bash
# Add all changes
git add .
# Commit the changes
git commit -m "Your commit message here"
# Push the changes to GitHub
git push origin master
```
**Rationale**: Breaking down your work into regular commits helps to keep track of changes and makes it easier to revert or reference previous states of the code.
Hope this helps you get started!
Artifacts Created
As mentioned, throughout the process different phases will produce different artifacts and require different intelligent feedback. For our Django to-do list application we will create all of the code and unit tests as the artifacts of this phase of the software development process as well as the documentation. There will be many code-related artifacts generated not by the LLM tool, but instead by Django’s project startup and management scripts. For this blog series we will only create a single document we call the “admin” guide.
Prerequisites
Create a git repository for our project
Copy all of the previously created assets to the base directory of this project
Initiate the virtual environment in the project. From inside the base directory run:
python3 -m venv .
Create the empty requirements.txt file:
touch requirements.txt
Test the activate.sh file by using it to start the virtual env
source ./activate.sh
Install black and flake8:
pip install black && pip freeze > requirements.txt pip install flake8 && pip freeze > requirements.txt
Install Django (also save it to the requirements.txt file)
pip install django && pip freeze > requirements.txt
Start the Django parent “project” (a project can have many “apps” in Django but all apps need a parent to manage them):
django-admin startproject todo_project .
Set up the Postgres backend. Since we are using the Docker Compose container, we should only have to install psycopg2-bin database adapter to get started:
pip install psycopg2-binary && pip freeze > requirements.txt
Update the Django settings for the Postgres backend:
Navigate to
todo_project/settings.py
and make the following changes:DATABASES = { 'default': { 'ENGINE': 'django.db.backends.postgresql', 'NAME': 'tododb', 'USER': 'todouser', 'PASSWORD': 'todopassword', 'HOST': 'localhost', 'PORT': '5432', } }
We are going to create a script that will be used inside the application container that will set up our database:
Create a file called database_setup.sql
Add the following to the file:
# Create database CREATE DATABASE tododb; # Create user with password CREATE USER todouser WITH PASSWORD 'todopassword'; # Set default encoding, tsearch2 (for full-text search) and timezone ALTER ROLE todouser SET client_encoding TO 'utf8'; ALTER ROLE todouser SET default_transaction_isolation TO 'read ommitted'; ALTER ROLE todouser SET timezone TO 'UTC'; # Grant all privileges on the database to the user GRANT ALL PRIVILEGES ON DATABASE tododb TO todouser; # Exit PostgreSQL command line \q
Save the file
Start an app for the todo project:
django-admin startapp todo_app
Now that we’ve created the “app” we need to add it to the installed apps in the todo_project:
Navigate to todo_project/settings.py and add the app to the INSTALLED_APPS list:
INSTALLED_APPS = [ "django.contrib.admin", "django.contrib.auth", "django.contrib.contenttypes", "django.contrib.sessions", "django.contrib.messages", "django.contrib.staticfiles", "todo_app", ]
Create a directory named templates in the todo_app folder. Add a single file called base.html. Paste the following contents and save:
<!-- todo_list/todo_app/templates/base.html --> <!-- Base template --> <!doctype html> <html lang="en"> <head> <!-- Required meta tags --> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <!-- Simple.css --> <link rel="stylesheet" href="https://cdn.simplecss.org/simple.min.css"> <title>Django To-do Lists</title> </head> <body> <div> <!-- When the title is clicked, it redirects to the index page --> <h1 onclick="location.href='{% url "index" %}'"> Django To-do Lists </h1> </div> <div> <!-- This block will contain page-specific content, replaced in hild templates --> {% block content %} This content will be replaced by different html code for each page. {% endblock %} </div> </body> </html>
Now we are ready to begin real coding on the app.
Tasks
Description: Since all tasks are coding and they all demand the use of ChatGPT-4 and the PQD pattern, we will omit that information from each task. We will also omit the description since it’s self-explanatory and has been covered in other articles. We will combine each sprint into a pattern. It’s important to note: that for this exercise we will keep the same “conversation” going in ChatGPT-4, so that LLM has a historical context that will allow us to develop the code iterati:
Sprint 1: Create framework for tasks. This will include the basic urls and model setup
Our Pattern Input:
You are an expert python programmer. You are working on a Django web application with a Postgres backend called a “Todo List Application”.
Create the urls.py for for the app “todo_app” and link it with the project urls.py file. Also create the data model for the application. There should be a model for a ToDoList and another model for ToDoItem. Also show the code to add these models to the Django Admin admin.py file.
The ToDoList model should have:
- A CharField named title that has a maximum length of 100 characters and should be unique.
The ToDoItem model should have:
- A CharField named title with a maximum length of 100 characters.
- A TextField named description that can be null or blank.
- A DateTimeField named created_date that has its value automatically set when the object is created.
- A DateTimeField named due_date that defaults to one week from the current time.
- A ForeignKey relationship to ToDoList with cascading delete.
Additionally, for both models:
- Implement the get_absolute_url method.
- Override the __str__ method to return a relevant string representation.
Also, include necessary imports and helper functions like one_week_hence to calculate the default value for the due_date of ToDoItem.
The code must be fast. The functions must have a docstring in python Google docstring format. Comment each line of code. Provide a few different solutions and explain the rational behind each.
Our Response Output (Code Only):
urls.py for todo_app:from django.urls import path
from . import views
urlpatterns = [
path('todolists/', views.ToDoListView.as_view(), name='todolists'),
path('todolist/<int:pk>/', views.ToDoListDetailView.as_view(),
ame='todolist_detail'),
path('todoitems/', views.ToDoItemListView.as_view(), name='todoitems'),
path('todoitem/<int:pk>/', views.ToDoItemDetailView.as_view(), name='todoitem_detail'),
]
urls.py for todo_project:
from django.urls import path, include
urlpatterns = [
path('todo/', include('todo_app.urls')), # Including urls of todo_app
# ... other urls
]
models.py for todo_app:
from django.db import models
from django.urls import reverse
import datetime
class ToDoList(models.Model):
""" Model representing a ToDo List """
title = models.CharField(max_length=100, unique=True) # Title of the ToDo
ist, unique so no two lists have the same name
def __str__(self):
""" String representation of the ToDoList """
return self.title
def get_absolute_url(self):
""" Returns the url to access a detail record for this ToDoList """
return reverse('todolist_detail', args=[str(self.id)])
class ToDoItem(models.Model):
""" Model representing a ToDo Item """
title = models.CharField(max_length=100) # Title of the ToDo item
description = models.TextField(null=True, blank=True) # Description of the
toDo item. Can be empty
created_date = models.DateTimeField(auto_now_add=True) # Date and time
hen the ToDo item was created
due_date = models.DateTimeField(default=datetime.datetime.now() + datetime.timedelta(weeks=1)) # Due date for the ToDo item, defaults to one week from creation
todolist = models.ForeignKey(ToDoList, on_delete=models.CASCADE) # ForeignKey relationship to ToDoList with cascading delete
def __str__(self):
""" String representation of the ToDoItem """
return self.title
def get_absolute_url(self):
""" Returns the url to access a detail record for this ToDoItem """
return reverse('todoitem_detail', args=[str(self.id)])
The admin.py for the todo_app:
from django.contrib import admin
from .models import ToDoList, ToDoItem
# Register the models for the admin site
admin.site.register(ToDoList)
admin.site.register(ToDoItem)
Sprint 2: Create the functionality for creating, editing and deleting tasks
Our Pattern Input:
Continuing on in the same role. You are going to implement new features
Create functionality to:
- create a todo list
- edit a todo list
- delete a todo list
- create tasks within in a todo list
- edit tasks within in a todo list
- delete tasks tasks within in a todo list
You must create the models, views, url handlers, admin model setup, url patterns, and html templates to achieve all this functionality in the current application you just provided.
The code must be fast. The functions must have a docstring in python Google docstring format. Comment each line of code. Provide a few different solutions and explain the rational behind each.
Our Response Output (Code Only):
todo_app/views.py:
from django.views.generic import ListView, DetailView, CreateView, UpdateView, DeleteView
from .models import ToDoList, ToDoItem
# For ToDoList
class ToDoListView(ListView):
model = ToDoList
template_name = 'todo_list.html'
class ToDoListCreateView(CreateView):
model = ToDoList
fields = ['title']
template_name = 'todolist_form.html'
success_url = '/todo/' # Redirect to todo list after creating
class ToDoListUpdateView(UpdateView):
model = ToDoList
fields = ['title']
template_name = 'todolist_form.html'
success_url = '/todo/' # Redirect to todo list after updating
class ToDoListDeleteView(DeleteView):
model = ToDoList
template_name = 'todolist_confirm_delete.html'
success_url = '/todo/' # Redirect to todo list after deletion
# For ToDoItem
class ToDoItemCreateView(CreateView):
model = ToDoItem
fields = ['title', 'description', 'due_date', 'todolist']
template_name = 'todoitem_form.html'
success_url = '/todo/' # Redirect to todo list after creating
class ToDoItemUpdateView(UpdateView):
model = ToDoItem
fields = ['title', 'description', 'due_date', 'todolist']
template_name = 'todoitem_form.html'
success_url = '/todo/' # Redirect to todo list after updating
class ToDoItemDeleteView(DeleteView):
model = ToDoItem
template_name = 'todoitem_confirm_delete.html'
success_url = '/todo/' # Redirect to todo list after deletion
todo_app/urls.py
from django.urls import path
from .views import (ToDoListView, ToDoListCreateView, ToDoListUpdateView, ToDoListDeleteView, ToDoItemCreateView, ToDoItemUpdateView, ToDoItemDeleteView)
urlpatterns = [
path('', ToDoListView.as_view(), name='todolist-list'),
path('create/', ToDoListCreateView.as_view(), name='todolist-create'),
path('<int:pk>/update/', ToDoListUpdateView.as_view(), name='todolist-
pdate'),
path('<int:pk>/delete/', ToDoListDeleteView.as_view(), name='todolist-delete'),
path('item/create/', ToDoItemCreateView.as_view(), name='todoitem-create'),
path('item/<int:pk>/update/', ToDoItemUpdateView.as_view(), name='todoitem-update'),
path('item/<int:pk>/delete/', ToDoItemDeleteView.as_view(), name='todoitem-delete'),
]
The final solution required us to merge and modify the todo_app/urls.py and todo_app/views.py into a single file and do a little refactoring to get to the result you’ll see in the next prompt.
Sprint 3: Front-end templates. Since the LLM didn’t actually provide us the code for the front end templates, we’re going to continue the conversation and have it generate them for us. We’ll need to supply the full urls.py and views.py code so it can reference them.
Our Pattern Input:
Continuing on in the same role. You are going implement the front-end templates Create the Django html templates for the provided application code so far. Here is the code:
(Code omitted for brevity)
The code must be fast. The functions must have a docstring in python Google docstring format. Comment each line of code. Provide a few different solutions and explain the rational behind each.Our Results Output (Code Only):
todo_list.html
html
{% extends "base.html" %}
{% block content %}
<h2>ToDo Lists</h2>
<a href="{% url 'todolist-create' %}">Create a new ToDo List</a>
<ul>
{% for todolist in object_list %}
<li><a href="{% url 'todolist_detail' todolist.pk %}">{{ todolist.title
}</a></li>
{% endfor %}
</ul>
{% endblock %}
todolist_form.html
html
{% extends "base.html" %}
{% block content %}
<form method="post">
{% csrf_token %}
{{ form.as_p }}
<input type="submit" value="Submit">
</form>
{% endblock %}
todolist_detail.html
html
{% extends "base.html" %}
{% block content %}
<h2>{{ object.title }}</h2>
<a href="{% url 'todolist-update' object.pk %}">Update</a>
<a href="{% url 'todolist-delete' object.pk %}">Delete</a>
{% endblock %}
todolist_confirm_delete.html
html
{% extends "base.html" %}
{% block content %}
<form method="post">
{% csrf_token %}
Are you sure you want to delete "{{ object.title }}"?
<input type="submit" value="Confirm">
</form>
{% endblock %}
todoitem_form.html
html
{% extends "base.html" %}
{% block content %}
<form method="post">
{% csrf_token %}
{{ form.as_p }}
<input type="submit" value="Submit">
</form>
{% endblock %}
todoitem_detail.html
html
{% extends "base.html" %}
{% block content %}
<h2>{{ object.title }}</h2>
<p>{{ object.description }}</p>
<a href="{% url 'todoitem-update' object.pk %}">Update</a>
<a href="{% url 'todoitem-delete' object.pk %}">Delete</a>
{% endblock %}
todoitem_confirm_delete.html
html
{% extends "base.html" %}
{% block content %}
<form method="post">
{% csrf_token %}
Are you sure you want to delete "{{ object.title }}"?
<input type="submit" value="Confirm">
</form>
{% endblock %}
Sprint 4: Marking tasks as done and due date functionality. We are going to rely heavily on the LLM and the historical context of the chat for this one, mostly out of morbid curiosity.
Our Pattern Input:
Continuing on in the same role. You are going implement new features and updated all code across the entire Django application.
The new features are marking the todo list items as done, and setting a due date for the checklist items.
The code must be fast. The functions must have a docstring in python Google docstring format. Comment each line of code. Provide a few different solutions and explain the rational behind each.Our Results Output (Code Only):
Update for models.py for done functionality:
from django.db import models
class ToDoItem(models.Model):
...
is_done = models.BooleanField(default=False) # Indicates whether the todo item is done or not
...
Update to views.py for doone functionality:
from django.http import HttpResponseRedirect
# View to mark a todo item as done or not done
def toggle_todo_done(request, pk):
"""Toggle the completion status of a ToDo item."""
item = get_object_or_404(ToDoItem, pk=pk)
item.is_done = not item.is_done
item.save()
return HttpResponseRedirect(request.META.get('HTTP_REFERER'))
Update to urls.py for done functionality:
urlpatterns = [
...
path('items/<int:pk>/toggle_done/', toggle_todo_done, name='todoitem-toggle_done'),
...
]
Update to html template todoitem_detail.html for due date functionality:
{% extends 'base.html' %}
{% block content %}
<h2>{{ object.title }}</h2>
<p>{{ object.description }}</p>
<!-- Display if the item is done or not -->
{% if object.is_done %}
<p>Status: Done</p>
{% else %}
<p>Status: Not Done</p>
{% endif %}
<!-- Display the due date -->
<p>Due Date: {{ object.due_date }}</p>
<!-- Button to toggle the status -->
<form action="{% url 'todoitem-toggle_done' object.pk %}" method="post"> {% csrf_token %}
<input type="submit" value="Toggle Done Status">
</form>
{% endblock %}
Conclusion
In summary, integrating large language model AI into agile software development can significantly enhance coding, testing, and collaboration workflows. Automated documentation creation saves time while continuous code reviews and testing feedback ensure quality. Tracking sprint progress and velocity provides data to optimize team effectiveness. Rather than replacing human developers, AI assists them by handling rote tasks, surfacing insights, and boosting productivity. With responsible development and thorough testing, AI promises to accelerate innovation cycles and help agile teams maximize customer value. The future of software lies in harmonious human-AI collaboration, and leveraging AI's natural language capabilities offers exciting benefits at every stage of agile delivery.