How AI Expedites Software Development
A case study to give a sense of current capabilities and examine where things will go from here.
Much has been made recently of Devin, the world’s first AI software engineer. And no, I don’t have access yet, but many of the capabilities exist in concept in GPT4, Claude, and other AIs, even if the process is a bit more manual.
Still, there are many wild views thrown out there as AI shows more and more competency, but it’s important to understand just exactly where things are. As such, I wanted to highlight tactically what AI coding can do with a focus toward providing an understandable explanation to non-coders.
Whether you are looking to use AI to build something, figure out where to invest, or worried about your current job getting automated away, it’s important to have a thorough and nuanced understanding of what AI can do.
So let’s take a look at a particular case that I feel is reflective of the value that AI tends to provide.
The Feature
I set out the other day to build a “search navigation box” that you’ll often see on sites, often controlled by hotkey.
As an example, here’s Notion’s search bar, which comes up once you hit CTRL+P:
This of course pops up over the content, but gives you a quick way to type a phrase, hit enter, and get to that page. As an example, hitting CTRL+P, then typing “fit” and then hitting ENTER will almost assuredly get me to my “Fitter” page which has all of my workouts logged from Strava, and so forth.
It’s a valuable feature, moreso for power users, but increasingly is how people navigate around apps. Case-in-point: the same shortcut works in VSCode and is responsible for ~60-70% of my navigation around there:
Since we have some mockups and the functionality isn’t too complicated, we can dive right into building this.
First, let’s examine how this would have gone in a pre-AI world.
A Pre-AI Build
As I’ve spent a few thousand hours with JavaScript and probably close to 1000 now with Node and React, I’d know generally how to build something like this. But, starting from scratch is hard and, with software, almost always the wrong move. So I’d go to DuckDuckGo, type “build a dynamic search bar react JavaScript” and then probably click on a link like this one.
Now, that site has a whole bunch of setup and it’s not quite doing what I would want it to do, so I’d probably spend 5-10 minutes reading that article, then jumping to another, et cetera. Maybe I’d copy some code but it’s all very boilerplate so I’d have a lot to adjust.
In adapting that example to my code, I’d run into several issues along the way. For example:
How do I filter the search options correctly once a user starts typing?
How do I ensure the textbox is auto-selected once the modal comes up?
How do I ensure the modal can be accessed from everywhere within my app?
How do I ensure each row navigates to the correct part of the site?
How do I connect it to a hotkey pair (e.g. CTRL+P)?
How do I let the user navigate up and down through the searched items?
Each of these are solvable, but could take 15-30 mins on the low end, average about an hour, and up to a few hours if one of these (or any other problems) was being particularly tricky.
So it’d be a big pain, and probably a full day of work to get it all working right and looking right.
Sounds like a pain, and honestly, as I was thinking about doing this, I procrastinated a bit because that’s still my mental model for how something like this would go.
But thankfully, we’ve got AI!
The AI Build
So instead of doing all that, I went over to my boy Chat (ChatGPT, paid GPT4 version) and said “hi, i am working on my node and react app. I'd like to implement a feature where hitting CTRL + P brings up a navigation/search menu. How would I implement this?”
And Chat give a great answer with some code that I tossed into my app. I needed to clarify a few things - that I wanted it to be a modal, which UI framework I was using, that I wanted a link associated with each entry/option, etc.
Each of these was a prompt that GPT basically hit in one shot. From there, I essentially had the functionality down, but there were a couple issues:
The modal wasn’t autoselecting the input box when it came up, so you’d have to click on the box before starting to type.
The modal was lingering a bit as the user navigated away, which led to some confusion as to whether the user’s steps had actually worked.
These too, were basically one-shotted by GPT4. It told me about ChakraUI’s initialFocusRef property that modals can have which solves that exact issue of the input not autoselecting.
Under the hood, the traditional isFocus property won’t work because of the timing of how/when things are loaded. Basically, the input isn’t yet on the page, so any attempt to focus on it does not occur.
And I kind of knew that was the issue and that there was likely a way around it, but instead of 30 minutes of searching, reading docs, and trying different things, I just asked Chat, “hmm, okay, I've got it close, but the input isn't being autoselected. How can I fix that?”
And I got right back the code for my component (this search widget) with the relevant changes baked in. It took 5 minutes, max?
One other thing I’ll call out is that Chat was able to intuit an intelligent search algorithm from the start. It’s initial code already built in handling for case sensitivity and searching for presence within the entire string rather than just the start. It also had the functionality to live-filter out items as the user typed setup right away, flawlessly. Any of these three things could have easily been stumbling blocks in a pre-AI world.
I made some other changes and did some tweaks to the display, the options to show, and few more things manually without deferring to Chat. So the end result is not entirely an AI creation. But a lot of the feature was created first by AI.
If you want, feel free to peruse my full chat with ChatGPT on the subject. It’s not long and gives you a sense of the ease of use.
All told, this probably took ~30 minutes to get done? Compare that to a full day in the pre-AI world, and it’s a crazy gain in efficiency.
The End Result
Here’s what we ended up with (again, after only 30 minutes):
This component isn’t overly complicated - it’s only about 140 lines of code, but it handles all the essentials. The code for the component is at the end of the post if you want to examine it.
And here’s a quick video of the interactions in action:
Or better yet, you can give it a try on Forward today!
What This Means
Hopefully this gives everyone a sense of the benefit of AI in Software Development today. It is incredibly powerful. As someone who has always been technical but never held the title of “Software Engineer” at an established company, GPT has been a huge force multiplier for me.
So can anyone code with ChatGPT by their side? It’s not a simple yes or no.
If you have an interest in doing so and learning as you use ChatGPT to build out a website or app, then yes. At least for now (but I suspect a while longer) coding with an AI still requires a human as the code base gets more complex.
If you try to use an AI to code something without a desire to learn or trying to learn what it’s doing, you are going to get to a point fairly quickly where the code is overly complex, unorganized, and bug-ridden. It may work on the surface, but you’ll face many unknown unknowns and be totally reliant on an AI to help you solve.
That said: using ChatGPT is a phenomenal way to learn how to code! You can ask it to explain its code blocks, ask it why certain things act the way they do, debate architectural choices, and more. By using it, you’ll see that maybe one day it could replace a developer entirely, but that there’s a lot more to building a good product than just writing the code.
Here is the code of the component for those interested:
import React, { useState, useEffect, useRef, useContext} from 'react';
import {
Modal, ModalOverlay, ModalContent, ModalHeader, ModalCloseButton, ModalBody, Input, useDisclosure, useColorModeValue, List, ListItem, Box, Text, VStack,
} from '@chakra-ui/react';
import { useNavigate } from 'react-router-dom';
import { ForwardContext } from '../../context/ForwardContext';
const NavigationSearchModal = () => {
const { isOpen, onOpen, onClose } = useDisclosure();
const { state } = useContext(ForwardContext);
const [filterText, setFilterText] = useState('');
const [selectedIndex, setSelectedIndex] = useState(0);
const navigate = useNavigate();
const inputRef = useRef(null);
const highlightColor = useColorModeValue('forwardBlue.50', 'forwardBlue.700');
/* Start with the homepage options */
const options = [
{ name: 'Forward Home', path: '/forward/', subtext: 'Homepage' },
{ name: 'Habits Home', path: '/forward/habits', subtext: 'Homepage' },
{ name: 'Tasks Home', path: '/forward/tasks', subtext: 'Homepage' },
{ name: 'Goals Home', path: '/forward/goals', subtext: 'Homepage' },
];
/* Add option for each life area */
for (const area of state.areas) {
options.push({ name: area.area_name, path: `/forward/area/${area.id}`, subtext: 'Life Area' });
}
/* And each project */
for (const project of Object.values(state.projects).flat()) {
options.push({ name: project.project_name, path: `/forward/project/${project.id}`, subtext: 'Project' });
}
/* And each habit */
for (const habit of Object.values(state.habits).flat()) {
options.push({ name: habit.habit_name, path: `/forward/habit/${habit.id}`, subtext: 'Habit' });
}
/* Lower priority: Settings and Updates */
options.push({ name: 'Settings', path: '/forward/settings', subtext: 'User Settings' });
options.push({ name: 'Updates', path: '/forward/updates', subtext: 'View Updates' });
// Filter options based on input text
const filteredOptions = options.filter((option) =>
option.name.toLowerCase().includes(filterText.toLowerCase())
);
useEffect(() => {
const openModal = () => onOpen();
if(isOpen) {
//Autoselect the input box
inputRef.current?.focus();
}
// Listen for the custom event
document.addEventListener('openNavigationSearchModal', openModal);
return () => {
document.removeEventListener('openNavigationSearchModal', openModal);
};
}, [onOpen]);
useEffect(() => {
if (isOpen) {
// Set input to be blank
setFilterText('');
// Focus on the input field when modal opens and reset selection
inputRef.current?.focus();
setSelectedIndex(0);
}
}, [isOpen]);
const handleOnClick = (path) => {
onClose(); // Close the modal
setTimeout(() => navigate(path), 150); // Navigate after a short delay
}
const handleKeyDown = (event) => {
if (event.key === 'Enter' && filteredOptions[selectedIndex]) {
// Navigate to the selected item's path
event.preventDefault(); // Prevent form submission or other default actions
onClose(); // Close the modal first
setTimeout(() => navigate(filteredOptions[selectedIndex].path), 150); // Navigate after a short delay
} else if (event.key === 'ArrowDown') {
// Prevent default to stop scrolling
event.preventDefault();
setSelectedIndex((prevIndex) => Math.min(prevIndex + 1, filteredOptions.length - 1));
} else if (event.key === 'ArrowUp') {
event.preventDefault();
setSelectedIndex((prevIndex) => Math.max(prevIndex - 1, 0));
}
};
return (
<Modal isOpen={isOpen} onClose={onClose} isCentered initialFocusRef={inputRef}>
<ModalOverlay />
<ModalContent borderRadius={0} style={{ marginTop: '0', position: 'absolute', top: '0' }}>
<ModalHeader>Fast Navigation</ModalHeader>
<ModalCloseButton />
<ModalBody onKeyDown={handleKeyDown}>
<Input
ref={inputRef}
borderRadius="0px"
placeholder="Type to filter..."
value={filterText}
onChange={(e) => setFilterText(e.target.value)}
mb={4}
/>
<Box as="div" style={{ maxHeight: '300px', overflowY: 'auto' }}>
<List spacing={3}>
{filteredOptions.map((option, index) => (
<VStack px={2}
key={index}
spacing={0}
bg={selectedIndex === index ? highlightColor : 'transparent'}
alignItems="flex-start"
cursor="pointer"
onClick={() => handleOnClick(option.path)}
>
<ListItem
key={index}
p={0}
fontWeight="bold"
>
{option.name}
</ListItem>
<Text fontSize="sm" ml={0} color="gray.500">{option.subtext}</Text>
</VStack>
))}
</List>
</Box>
</ModalBody>
</ModalContent>
</Modal>
);
};
export default NavigationSearchModal;