The article is about the realities of using ChatGPT to automate the writing of Java code, with a specific example and several conclusions on how ChatGPT would change the development market. I want to show you how, in our day, we can write every layer of Spring framework application without writing any lines of code by hand.
Introduction
There are opposed opinions among developers and IT company leaders regarding the current and future use of AI tools for code generation. Some predict that AI will take over a significant portion of work shortly, while others don’t see GPT as a human competitor, not even in the next quarter-century.
To support their argument that AI can effectively replace humans, the first group provides a couple of video examples where a simple application is written in real-time using ChatGPT in just 10 minutes. They also mention historical parallels with automation in other fields, where progress reduced human involvement by 5–7 times.
The second camp opposes the first by presenting the argument that people can write better code without any assistance from AI. They provide various arguments for the indispensability of humans in the development process, with the most popular ones being:
- The important role of human communication;
- The difficulties of formulations the requirements by the client;
- The creative process needs, which can only be performed directly by a human.
So, who is right? When will artificial intelligence replace all of us? And what is the most appropriate comparison for ChatGPT today?
Just a few words about theory to understand practice
To process a code-writing request, ChatGPT goes through a vast amount of data, including open-source code repositories where elements of the seeking solution or the entire solution likely exist. Wire internal graph relying on this data, the model navigates its “labyrinth,” attempting to predict the probability of determining the appropriateness of lexical units (variables, methods, code snippets) that, when sequentially arranged, make up the application code in response to our request.
However, the result is not stable. The reason is that the model essentially “rolls the dice” each time when making predictions for this probability. While considering the probability, it doesn’t guarantee choosing the element or combination of elements with the highest predicted probability. Often, by slightly changing the request or introducing an additional parameter, or even without making any changes, we can obtain a different, sometimes significantly different, result from the previous one.
What does work with ChatGPT look like?
You can take a large task and feed it to ChatGPT as it is. It will process it and provide a solution. But if we try to make a big request, we will receive:
- More abstract solutions that will require customization;
- More unsuitable solution that will require rework;
- In the end, the suggested option is not guaranteed to work.
As a result, you will receive long nights of debug sessions.
In practice, a large task is typically divided into smaller, more specific, and narrower sub-tasks, which are then presented to ChatGPT.
So, let’s divide the creation of all layers of the Spring application and feed it to ChatGPT.
Practice
When creating a typical CRUD controller, a developer usually does several things.
- Create Entity;
- Make migration of data to DB;
- Create DTO for the Entity;
- Create Repository layer;
- Create Service layer;
- Create a Controller layer.
Create Entity
Prompt to ChatGPT:
“create for me the entity class of User with Long id, firstName, lastName, phoneNumber, UUID keycloakId, @ManyToOne relation with class Department.”
The response:
@Entity
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String firstName;
private String lastName;
private String phoneNumber;
private UUID keycloakId;
@ManyToOne
@JoinColumn(name = "department_id") // Assuming a foreign key column named 'department_id'
private Department department;
// Constructors, getters, and setters
public User() {
}
public User(String firstName, String lastName, String phoneNumber, UUID keycloakId, Department department) {
this.firstName = firstName;
this.lastName = lastName;
this.phoneNumber = phoneNumber;
this.keycloakId = keycloakId;
this.department = department;
}
// Getters and setters for all fields
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String getPhoneNumber() {
return phoneNumber;
}
public void setPhoneNumber(String phoneNumber) {
this.phoneNumber = phoneNumber;
}
public UUID getKeycloakId() {
return keycloakId;
}
public void setKeycloakId(UUID keycloakId) {
this.keycloakId = keycloakId;
}
public Department getDepartment() {
return department;
}
public void setDepartment(Department department) {
this.department = department;
}
}
But we want to make the same result but using Lombok:
Create DTO
Prompt to ChatGPT:
“Can you create a DTO class for this entity?”
Response from ChatGPT:
public class UserDTO {
private Long id;
private String firstName;
private String lastName;
private String phoneNumber;
private UUID keycloakId;
Private Long departmentId; // Assuming you want to transfer the department ID
public UserDTO() {
}
public UserDTO(Long id, String firstName, String lastName, String phoneNumber, UUID keycloakId, Long departmentId) {
this.id = id;
this.firstName = firstName;
this.lastName = lastName;
this.phoneNumber = phoneNumber;
this.keycloakId = keycloakId;
this.departmentId = departmentId;
}
// Getters and setters for all fields
}
Create Repository
Prompt to ChatGPT:
“Can you create a repository for this entity?”
Response from ChatGPT:
import org.springframework.data.jpa.repository.JpaRepository;
public interface UserRepository extends JpaRepository<User, Long> {
// You can add custom query methods here if needed
}
Create Service
Prompt to ChatGPT:
Can you create for this entity CRUD Service with the next methods:
- Received pageable list of users;
- Receive user by Id;
- Delete User by Id;
- Create a new User.
Response from ChatGPT:
@Service
public class UserService {
private final UserRepository userRepository;
@Autowired
public UserService(UserRepository userRepository) {
this.userRepository = userRepository;
}
// Method to receive a pageable list of users
public Page<User> getAllUsers(Pageable pageable) {
return userRepository.findAll(pageable);
}
// Method to receive a user by Id
public Optional<User> getUserById(Long userId) {
return userRepository.findById(userId);
}
// Method to delete a user by Id
public void deleteUserById(Long userId) {
userRepository.deleteById(userId);
}
// Method to create a new user
public User createUser(User user) {
return userRepository.save(user);
}
}
Make migration of data to DB
Prompt to ChatGPT:
“Can you create the migration of this entity to the database?”
Response from ChatGPT:
-- Create the User table
CREATE TABLE user (
id BIGINT PRIMARY KEY AUTO_INCREMENT, -- Use the appropriate data type for your database
first_name VARCHAR(255) NOT NULL,
last_name VARCHAR(255) NOT NULL,
phone_number VARCHAR(20),
keycloak_id UUID, -- Use the appropriate data type for UUID in your database
department_id BIGINT, -- Use the appropriate data type for the foreign key reference
FOREIGN KEY (department_id) REFERENCES department(id) -- Make sure to reference the correct table and column names
);
Create Controller
Prompt to ChatGPT:
“Can you create a Controller for this Service?”
Response from ChatGPT:
@RestController
@RequestMapping("/users")
public class UserController {
private final UserService userService;
@Autowired
public UserController(UserService userService) {
this.userService = userService;
}
// Get a pageable list of users
@GetMapping
public ResponseEntity<Page<User>> getAllUsers(Pageable pageable) {
Page<User> users = userService.getAllUsers(pageable);
return new ResponseEntity<>(users, HttpStatus.OK);
}
// Get a user by Id
@GetMapping("/{userId}")
public ResponseEntity<User> getUserById(@PathVariable Long userId) {
return userService.getUserById(userId)
.map(user -> new ResponseEntity<>(user, HttpStatus.OK))
.orElseGet(() -> new ResponseEntity<>(HttpStatus.NOT_FOUND));
}
// Create a new user
@PostMapping
public ResponseEntity<User> createUser(@RequestBody User user) {
User createdUser = userService.createUser(user);
return new ResponseEntity<>(createdUser, HttpStatus.CREATED);
}
// Delete a user by Id
@DeleteMapping("/{userId}")
public ResponseEntity<Void> deleteUser(@PathVariable Long userId) {
userService.deleteUserById(userId);
return new ResponseEntity<>(HttpStatus.NO_CONTENT);
}
}
Create Mock JSON file of created data
Prompt to ChatGPT:
“Can you create from the DTO class json file with mock data?”
Response from ChatGPT:
[
{
"id":1,
"firstName":"John",
"lastName":"Doe",
"phoneNumber":"123-456-7890",
"keycloakId":"5e7e4196-4a10-11ec-8d3d-0242ac130003",
"departmentId":1
},
{
"id":2,
"firstName":"Alice",
"lastName":"Smith",
"phoneNumber":"987-654-3210",
"keycloakId":"6d7e4196-4a10-11ec-8d3d-0242ac130003",
"departmentId":2
},
{
"id":3,
"firstName":"Bob",
"lastName":"Johnson",
"phoneNumber":"555-555-5555",
"keycloakId":"7d7e4196-4a10-11ec-8d3d-0242ac130003",
"departmentId":1
}
]
Create Unit tests for Service
Prompt to ChatGPT:
“Can you create unit tests for the service class?”
Response from ChatGPT:
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import java.util.Optional;
import static org.mockito.Mockito.*;
public class UserServiceTest {
@InjectMocks
private UserService userService;
@Mock
private UserRepository userRepository;
@BeforeEach
public void init() {
MockitoAnnotations.initMocks(this);
}
@Test
public void testGetAllUsers() {
// Mock data
Pageable pageable = PageRequest.of(0, 10);
Page<User> users = mock(Page.class);
when(userRepository.findAll(pageable)).thenReturn(users);
Page<User> result = userService.getAllUsers(pageable);
verify(userRepository, times(1)).findAll(pageable);
}
@Test
public void testGetUserById() {
// Mock data
Long userId = 1L;
User user = new User();
when(userRepository.findById(userId)).thenReturn(Optional.of(user));
Optional<User> result = userService.getUserById(userId);
verify(userRepository, times(1)).findById(userId);
}
@Test
public void testGetUserById_NotFound() {
// Mock data
Long userId = 1L;
when(userRepository.findById(userId)).thenReturn(Optional.empty());
Optional<User> result = userService.getUserById(userId);
verify(userRepository, times(1)).findById(userId);
}
@Test
public void testDeleteUserById() {
// Mock data
Long userId = 1L;
userService.deleteUserById(userId);
verify(userRepository, times(1)).deleteById(userId);
}
@Test
public void testCreateUser() {
// Mock data
User user = new User();
when(userRepository.save(user)).thenReturn(user);
User result = userService.createUser(user);
verify(userRepository, times(1)).save(user);
}
}
Print project structure for a better understanding of what we generated
Prompt to ChatGPT:
“Can you generate the whole code structure of this project?”
Response from ChatGPT:
Pros and cons
Based on the above, I can determine the pros and cons of using AI at the current level at which we have it.
It can be useful for increasing the time spent coding. Comparing the example in this article, I can say that if I had to write this structure by hand, I think that I would spend 2 times more. I can say that the model layer increases the number of classes, so the difference between hand programming and programming using AI will increase.
The other plus is that ChatGPT, when it generates something, describes to you each tool that it used and tells you for what purpose it was used. Also, you can ask it to remove some tools or use the other one.
The good thing is that ChatGPT, within one chat, remembers the whole story of your “conversation.” And it creates the answer, remembering chat history.
You can ask everything: generate whole class, method, script for CI/CD pipeline, Jenkins script, application properties file, how to create docker files, etc.
But the main problem is that you can’t trust it because chatGPT doesn’t have a problem with lying. It can generate something and say it will surely work, but that will be wrong. Every time we ask something, we must remember that every request, even the same one, will return a different response. So, in any case, the developer must decide what the result is supposed to look like and be responsible for the project build, tests run, whether the code contains any bugs, and so on.
Conclusion
So, in the end, what is ChatGPT essentially? Considering its behavior and functioning, ChatGPT is now more like a genie in a lamp. It has a good foundation, excellent powerful tools, and can find the right solution, but the request must be extremely precise to get the desired result. Even if you are accurate, it’s not guaranteed that you will succeed on the first try, and something undesirable may still come out.
Even though not everything is smooth with coding in ChatGPT at the moment, this technology can be integrated with plugins for IDEs. In the future, once they are refined, they can assist developers in writing code much better than ChatGPT does now in a question-and-answer format.
Currently, we have a production-ready solution from Microsoft. I am talking about Github Copilot, which can suggest and generate code blocks for you based on the method name. This tool is not the same as ChatGPT, but it is also very useful and decreases the time of coding some basic solutions in projects. And again, they can assist but not write the whole project for the developer, replacing them.
My predictions
Will ChatGPT eventually leave developers without a job? – No! Currently, ChatGPT in production is not an engineer but an engineer’s tool.
Will it automate the process of writing code over time and significantly reduce this stage of development? – Yes, definitely.
Leave a comment