Using ChatGPT to Accelerate Software Development
Focus on organization, but also have it build the small bits...
I started using ChatpGPT as soon as it became available with version 3.5. When version 4 was announced, I immediately signed up for the ChatGPTPlus service to have access (albeit limited).
My initial review of 3.5 was laughable. It flat out lied about things and gave fake code. One example was to have it generate an MQTT client/server in C#. It used libraries that actually don’t exist (but looked like they should).
I dismissed the entire thing as a fun little toy, but not a serious one.
Then v4 arrived and after testing it, my entire world turned inside out. Now only is it delivering real value with general questions, but its ability to generate legitimate code, to work through complex coding problems, and also add unit testing, is astounding.
One general request was installing some software that required PostgreSQL. I couldn’t finish the install, so I asked ChatGPT for help. It had two suggestions, the first of which worked perfectly. Of course, the answer came from a Stack Overflow post, but when I went to look at that reference, it was much more challenging to discern the correct steps to take where ChatGPT filtered the message down to the exact actions.
After that I decided to test its ability to design and generate real code. As a seasoned developer in C#, a popular programming language developed by Microsoft, I decided to stick with what I know. The project I decided to tackle was building an Interactive Fiction (IF) platform—a type of software used to create and play text-based adventure games—hosted on GitHub, using the C# language within the .NET Core framework. For those unfamiliar, .NET Core is a free, cross-platform, open-source developer platform for building different types of applications.
I first walked through several conversations about what I expected the platform to contain, including: a parser, a world model based on an in-memory graph data store, a story file, a game runner, a grammar library, a standard library (for IF specific things), and more. I then dug into the world model and an in-memory graph and over four iterations, came up with a very workable structure that includes World, Node, Edge, GraphProperty, and StateChangeEventType.
Then I followed up with conversations about the other elements and here’s where things get challenging. With the current version of ChatGPT, we're restricted to 25 prompts within a 3-hour period. Essentially, a 'prompt' is an input or question that you feed into ChatGPT. This limit means you can only initiate 25 exchanges or inputs with the AI in a span of 3 hours. One of the consequences of this restriction is that ChatGPT will start to 'forget' what you’re working on towards the end of those 25 prompts. This is due to the model's design which does not retain any knowledge of past interactions for privacy reasons. This can become challenging when you're working on a large project that spans several prompts or interactions over time.
The best way to continue is to restart but front-load the current state of your code. I would start a new iteration with, “We’re building an Interactive Fiction platform and I’m going to share the current code. There’s no need to respond until I ask you to.”
You can’t paste all of the code in one shot since there’s a size limit. I would break up the code into as large of chunks as possible, so I use the fewest actions. Once all the code is in its “brain”, I restart the conversation about additions and changes.
using System;
using System.Collections.Generic;
using System.Linq;
using GrammarLibrary;
namespace ParserLibrary
{
public class Parser
{
private Grammar _grammar;
public Parser(Grammar grammar)
{
_grammar = grammar;
}
public void Parse(string input)
{
List<string> words = input.Split(' ', StringSplitOptions.RemoveEmptyEntries).ToList<string>();
List<Token> tokens = new List<Token>();
foreach (string word in words)
{
if (_grammar.Sentences.ContainsKey(word))
{
tokens.AddRange(_grammar.Sentences[word][0]);
}
else
{
tokens.Add(new Token(TokenType.Unknown, word));
}
}
if (IsValidSentence(tokens))
{
ExecuteAction(tokens);
}
else
{
Console.WriteLine("Invalid input. Please try again.");
}
}
private bool IsValidSentence(List<Token> tokens)
{
foreach (var verbEntry in _grammar.Sentences)
{
foreach (var sentence in verbEntry.Value)
{
if (tokens.Count == sentence.Count)
{
bool isMatch = true;
for (int i = 0; i < tokens.Count; i++)
{
if (tokens[i].Type != sentence[i].Type)
{
isMatch = false;
break;
}
}
if (isMatch)
{
return true;
}
}
}
}
return false;
}
private void ExecuteAction(List<Token> tokens)
{
foreach (var token in tokens)
{
if (token.Delegate != null)
{
token.Delegate();
break;
}
}
}
}
}
As you can see, ChatGPT does an excellent job of working within your design constraints.
I’ll pause here to talk about what it’s like working with ChatGPT on a project like this. First of all, you absolutely need to understand the domain. Although ChatGPT understands elements of IF, it only understands what’s existed within the Internet when it was loading its AI. It knows about existing platforms like TADS and Inform and probably a lot of free form text on the subject. It won’t have any idea on new concepts like building the world model on an in-memory graph or on having a separate Text Service to combine and emit text. Those are my own thoughts and I needed to spell out their design to ChatGPT.
Because I understand the IF platform “domain” I am able to guide ChatGPT in the directions I need, to discern problems with the code its generated, and to make adjustments in my prompts. But a complex problem requires domain knowledge. There’s no way around that.
I have tried simpler applications like “Create a MUD Client in C#” and shockingly, ChatGPT can do that in a single prompt.
I work as a software architect in my real job and my company has restricted use of ChatGPT with client work. There are privacy, ethics, and legal concerns on using a tool like ChatGPT. A lot of code is very generic, but some code can absolutely be proprietary. A client could claim “harm” if proprietary code showed up in another user’s response. There are ethical concerns as well. An AI like ChatGPT could be used to develop misinformation that looked like it was real information. This could lead to people learning things that aren’t true, even though the AI makes it sound highly plausible. Used in advertising or politics, a bad actor could micro-target consumers or voters into purchasing something or voting against their interests or not voting at all.
However, there are places where it’s still a viable tool. I’m a very serious advocate for Domain-Driven Design, a set of activities and engineering patterns to help break down complex applications.
DDD software has some basic patterns like Value Object, and these are so common that they could never be “copyrighted” without a challenge. Things like EmailAddress or PhoneNumber or StreetAddress. Not only can you leverage ChatGPT to generate these classes (in your language choice) you can also have it generate a full suite of unit tests in the test harness of your choice.
using System;
using System.Text.RegularExpressions;
public class EmailAddress : IEquatable<EmailAddress>
{
private readonly string _value;
public EmailAddress(string value)
{
if (string.IsNullOrWhiteSpace(value))
{
throw new ArgumentException("Email address cannot be null or empty.", nameof(value));
}
if (!IsValidEmail(value))
{
throw new ArgumentException("Email address is not valid.", nameof(value));
}
_value = value;
}
public static bool IsValidEmail(string email)
{
const string emailPattern =
@"^(([\w-]+\.)+[\w-]+|([a-zA-Z]{1}|[\w-]{2,}))@"
+ @"((([0-1]?[0-9]{1,2}|25[0-5]|2[0-4][0-9])\.([0-1]?[0-9]{1,2}|25[0-5]|2[0-4][0-9])\."
+ @"([0-1]?[0-9]{1,2}|25[0-5]|2[0-4][0-9])\.([0-1]?[0-9]{1,2}|25[0-5]|2[0-4][0-9])){1}|"
+ @"([a-zA-Z]+[\w-]+\.)+[a-zA-Z]{2,4})$";
return Regex.IsMatch(email, emailPattern);
}
public override bool Equals(object obj)
{
return Equals(obj as EmailAddress);
}
public bool Equals(EmailAddress other)
{
return other != null && _value.Equals(other._value, StringComparison.OrdinalIgnoreCase);
}
public override int GetHashCode()
{
return _value.ToLowerInvariant().GetHashCode();
}
public override string ToString()
{
return _value;
}
public static bool operator ==(EmailAddress left, EmailAddress right)
{
return Equals(left, right);
}
public static bool operator !=(EmailAddress left, EmailAddress right)
{
return !Equals(left, right);
}
}
I stopped the code generation because I prefer the MSTest library.
using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;
[TestClass]
public class EmailAddressTests
{
[TestMethod]
public void Constructor_ValidEmail_SetsValue()
{
// Arrange
string validEmail = "test@example.com";
// Act
EmailAddress emailAddress = new EmailAddress(validEmail);
// Assert
Assert.AreEqual(validEmail, emailAddress.ToString());
}
[TestMethod]
public void Constructor_InvalidEmail_ThrowsArgumentException()
{
// Arrange
string invalidEmail = "invalid-email";
// Act & Assert
Assert.ThrowsException<ArgumentException>(() => new EmailAddress(invalidEmail));
}
[TestMethod]
public void Constructor_NullOrEmptyEmail_ThrowsArgumentException()
{
// Arrange
string nullEmail = null;
string emptyEmail = string.Empty;
// Act & Assert
Assert.ThrowsException<ArgumentException>(() => new EmailAddress(nullEmail));
Assert.ThrowsException<ArgumentException>(() => new EmailAddress(emptyEmail));
}
[TestMethod]
public void IsValidEmail_ValidEmail_ReturnsTrue()
{
// Arrange
string validEmail = "test@example.com";
// Act
bool isValid = EmailAddress.IsValidEmail(validEmail);
// Assert
Assert.IsTrue(isValid);
}
[TestMethod]
public void IsValidEmail_InvalidEmail_ReturnsFalse()
{
// Arrange
string invalidEmail = "invalid-email";
// Act
bool isValid = EmailAddress.IsValidEmail(invalidEmail);
// Assert
Assert.IsFalse(isValid);
}
[TestMethod]
public void Equals_SameEmailAddresses_ReturnsTrue()
{
// Arrange
string email1 = "test@example.com";
string email2 = "test@example.com";
EmailAddress emailAddress1 = new EmailAddress(email1);
EmailAddress emailAddress2 = new EmailAddress(email2);
// Act & Assert
Assert.IsTrue(emailAddress1.Equals(emailAddress2));
Assert.IsTrue(emailAddress1 == emailAddress2);
}
[TestMethod]
public void Equals_DifferentEmailAddresses_ReturnsFalse()
{
// Arrange
string email1 = "test@example.com";
string email2 = "test2@example.com";
EmailAddress emailAddress1 = new EmailAddress(email1);
EmailAddress emailAddress2 = new EmailAddress(email2);
// Act & Assert
Assert.IsFalse(emailAddress1.Equals(emailAddress2));
Assert.IsFalse(emailAddress1 == emailAddress2);
}
[TestMethod]
public void GetHashCode_SameEmailAddresses_ReturnsSameHashCode()
{
// Arrange
string email1 = "test@example.com";
string email2 = "test@example.com";
EmailAddress emailAddress1 = new EmailAddress(email1);
EmailAddress emailAddress2 = new EmailAddress(email2);
// Act & Assert
Assert.AreEqual(emailAddress1.GetHashCode(), emailAddress2.GetHashCode());
}
[TestMethod]
public void GetHashCode_DifferentEmailAddresses_ReturnsDifferentHashCode()
{
// Arrange
string email1 = "test@example.com";
string email2 = "test2@example.com";
EmailAddress emailAddress1 = new EmailAddress(email1);
EmailAddress emailAddress2 = new EmailAddress(email2);
// Act
int hashCode1 = emailAddress1.GetHashCode();
int hashCode2 = emailAddress2.GetHashCode();
// Assert
Assert.AreNotEqual(hashCode1, hashCode2);
}
}
And if your company does have some odd invariant rule to control the logic within one of these Value Objects, you can just ask ChatGPT to add that wrinkle, regenerate the code and unit tests, and you’re done. You have taken a common repetitive coding task and reduced it to one or two ChatGPT prompts. This is one of the things I think can and will be added to DevOps processes. We will see a set of prompt files that produce code and unit tests on the fly and that code is never stored in source control.
Back to my IF platform. If you peruse the GitHub code, you can see it helped me generate a ton of complex code in hours, maybe days. I can say with certainty that I would not have been able to do this on my own. There are concepts in here that I learned as the code was being generated. I can say my knowledge of graph data is significantly stronger than it was prior to this exercise.
This brings me to a pivotal aspect of my experience with ChatGPT: its potential as a learning tool. In my case, while I understood the basic concepts of Edges and Nodes—fundamental elements in graph theory—the actual implementation of an in-memory graph was beyond my prior knowledge and experience. However, by leveraging ChatGPT to generate code based on my conceptual understanding, I was able to gain valuable insights into how such a graph could be implemented in practice. It was as though I had an experienced colleague guiding me through the process, helping to solidify and expand my understanding. This is why I firmly believe that ChatGPT can not only make you a more efficient programmer but also a more knowledgeable one. It has a unique ability to teach new concepts in a polite and positive manner, and at any time when you encounter something unfamiliar, you can delve into it, twist it, change it, and continue to learn.
In summary, ChatGPT v4 and future and custom versions are already changing the world. If you’re in IT, you absolutely need to investigate ways this will integrate with existing “ways of working”.
I see accelerated software development where common and well-defined use cases can be prompted.
I see reduction in code where common and well-defined use cases can be prompted as a part of testing and deployment.
I see reduction in code and an increase in reliability by generating unit tests using prompts.
I see junior programmers learning faster by conversing with an AI instead of waiting on other team members to show them the ropes.
I see onboarding in complex environments accelerated by turning an AI into a learning platform.
There are limitless possibilities, many are good, some are bad. Some are very bad.
But I do believe the invention of ChatGPT v4 is a sea-change in human progress similar to the industrial age, the computer age, and the Internet.
(by the way, ChatGPT helped me hone this article)