Physical Address
304 North Cardinal St.
Dorchester Center, MA 02124
Physical Address
304 North Cardinal St.
Dorchester Center, MA 02124
JetBrains recently announced they would be integrating an AI Assistance into their IDEs. With this, I download the Early Access release of Intelij to have a go. There has been a lot of talk about generative AI within the Software Engineering community, so I wanted to view it first hand.
My intention was to try to create a service without writing any code. I accepted all code as given for this. In reality the best use would be to pick and choose and use it to enhance, not accept as is, but that wasn’t the goal of this exercise.
You have to enable AI Assistant by signing in, once done you have a new dialog panel in the IDE where you can begin typing. The results are displayed in the pane and can be copied and pasted into the main editor as required. I went with a curved ball (or so I thought) early on and asked,
Implement a BASIC interpreter
After a very quick pause, I was impressed and surprised to see on screen a number of classes making up the basis of a simple BASIC interpreter in Java. It has split out the code into a number of classes, Tokens, Lexer, Parser and code to evaluate a simple addition and subtraction, all with a main method. I cut and paste into the IDE, clicked run, and was able to enter 1+2 and get a result. I did ask it to extend this to include a print statement, which it did, but this did not compile. The AI did say it was an indication on how to do it and it might not be fully functional, so at least it knew! Certainly not a bad starting point and impressive for such a broad ask.
With the code, some text saying it was a simple example, what it was based on and reference to other material including books to learn more about this topic.
The main part I wanted to explore was could I use this to build a simple Microservice without having to write any code? The goal was to create a SpringBoot service with a TODO list API.
Create a new multi module spring boot app called todo-list-app with 2 modules, todo-list-service todo-list-it using java 17 with a CRUD API for a todo list
A lot to ask, and in return I got the pom files (parent and module), file structure and some classes;
Along with the code came a summary providing context of what was supplied and how to use it.
The REST controller didn’t contain any APIs, only comment for CRUD, so I asked
Can you add CRUD operations to the Todo Controller class?
AI Assistance duly obliged
@RestController
@RequestMapping("/api/todos")
public class TodoController {
@Autowired
private TodoRepository todoRepository;
// Create a new Todo
@PostMapping
public ResponseEntity<Todo> create(@RequestBody Todo todo) {
return new ResponseEntity<>(todoRepository.save(todo), HttpStatus.CREATED);
}
// Read all Todos
@GetMapping
public ResponseEntity<List<Todo>> getAllTodos() {
return new ResponseEntity<>(todoRepository.findAll(), HttpStatus.OK);
}
// Read a single Todo
@GetMapping("/{id}")
public ResponseEntity<Todo> getTodoById(@PathVariable Long id) {
Optional<Todo> todo = todoRepository.findById(id);
return todo.map(value -> new ResponseEntity<>(value, HttpStatus.OK)).orElseGet(() -> new ResponseEntity<>(HttpStatus.NOT_FOUND));
}
// Update a Todo
@PutMapping("/{id}")
public ResponseEntity<Todo> update(@PathVariable Long id, @RequestBody Todo newTodo) {
return todoRepository.findById(id)
.map(todo -> {
todo.setTitle(newTodo.getTitle());
todo.setDetail(newTodo.getDetail());
return new ResponseEntity<>(todoRepository.save(todo), HttpStatus.OK);
})
.orElseGet(() -> new ResponseEntity<>(HttpStatus.NOT_FOUND));
}
// Delete a Todo
@DeleteMapping("/{id}")
public ResponseEntity<Void> delete(@PathVariable Long id) {
todoRepository.deleteById(id);
return new ResponseEntity<>(HttpStatus.NO_CONTENT);
}
}
It did seem to forget some elements of the model, so a couple of tweaks required to change setDetail (for some reason it came out as setDescription)
All in all, not bad, saved me a fair bit of typing. The code calling on the repository class to act on the data.
Can you implement some unit tests please
And it did for the controller class, but not for each method.
@WebMvcTest(TodoController.class)
public class TodoControllerTest {
@Autowired
private MockMvc mockMvc;
@Autowired
private ObjectMapper objectMapper;
@MockBean
private TodoRepository todoRepository;
private Todo todo;
@BeforeEach
void setUp() {
todo = new Todo();
todo.setId(1L);
todo.setTitle("Test");
todo.setDetail("Test detail");
}
@Test
public void shouldFetchOneTaskById() throws Exception {
given(todoRepository.findById(1L)).willReturn(Optional.of(todo));
mockMvc.perform(get("/api/todos/1")
.contentType("application/json"))
.andExpect(status().isOk());
}
@Test
public void shouldCreateTask() throws Exception {
given(todoRepository.save(todo)).willReturn(todo);
mockMvc.perform(post("/api/todos")
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(todo)))
.andExpect(status().isCreated());
}
}
Could you add tests for all CRUD operations
And it did (omitted here). I am not evaluating the code generated, that’s for another post, the purpose here to to see what can be done with a few simple commands.
I asked it to add Swagger, it did, but configuration versioning issues prevent this from working with spring fox, so asked to switch to spring doc. After a bit of tweaking. I now had a service up and running, using H2 in-memory database and could try out the API using swagger!
Next I tried to up the ante with some Cucumber testing. It gave me a nice write up about Cucumber and how to set up the POM file and generated mew a feature file
Feature: Todo management
Scenario: User can create a todo
Given an existing user with id "1"
When user with id "1" creates a todo with title "Test title" and description "Test description"
Then a new todo should be created with user id "1", title "Test title" and description "Test description"
# Create more Scenarios for Read, Update, and Delete operations similar to this
And a suggestion for me to add more. It gave me a file for the test runner and step definitions stubs.
Can you implement the step definitions
Yep!
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class TodoStepDefinitions {
@LocalServerPort
private int port;
@Autowired
private TestRestTemplate restTemplate;
private Todo newTodo;
private ResponseEntity<Todo> response;
@Given("an existing user with id {string}")
public void an_existing_user_with_id(String userId) {
newTodo = new Todo();
newTodo.setUserId(userId);
}
@When("user with id {string} creates a todo with title {string} and description {string}")
public void user_with_id_creates_a_todo_with_title_and_description(String userId, String title, String description) {
newTodo.setTitle(title);
newTodo.setDescription(description);
HttpEntity<Todo> request = new HttpEntity<>(newTodo);
String url = "http://localhost:" + port + "/todos";
response = restTemplate.exchange(url, HttpMethod.POST, request, Todo.class);
}
@Then("a new todo should be created with user id {string}, title {string} and description {string}")
public void a_new_todo_should_be_created_with_user_id_title_and_description(String userId, String title, String description) {
assertEquals(HttpStatus.CREATED, response.getStatusCode());
Todo todo = response.getBody();
assertEquals(userId, todo.getUserId());
assertEquals(title, todo.getTitle());
assertEquals(description, todo.getDescription());
}
}
I had issues running the tests, took me a fair bit of time and tweaking to get it working, possibly down to maven and versions, I find maven somewhat clunky to use at the best of times, but it certainly gave me a great starting point.
With that I stopped and had a look at a few more general features.I
I have not yet extensively used the service, but I wanted to try out a few more general features. The goal he was to auto generate a container class, implement a builder class and unit tests. It was pretty good at this, and I could see the value in this, it saved me a lot of typing.
Create a class for Name, Address, PostCode, County and Country
It created me 5 classes, one for each. I dint try to tweak the request, so went with it. Below of the Address class
public class Address {
private String line1;
private String line2;
private PostCode postCode;
private County county;
private Country country;
}
Create a builder for this
It happily created me builder classes for each of them. Below is the address builder. It didn’t implement all methods, informing me to repeat the pattern. I have no doubt if I’d asked it to, it would implement the all of them.
public static class AddressBuilder {
private String line1;
private String line2;
private PostCode postCode;
private County county;
private Country country;
public AddressBuilder line1(String line1) {
this.line1 = line1;
return this;
}
// Repeat the pattern for other attributes
// ...
public Address build() {
return new Address(this);
}
}
Write a test for the builder class
public class AddressTests {
@Test
public void testBuilder() {
// create the required objects
PostCode postCode = new PostCode.PostCodeBuilder().code("12345").build();
County county = new County.CountyBuilder().name("Example County").build();
Country country = new Country.CountryBuilder().name("Example Country").build();
// Use the builder to create an address object
Address testAddress = new Address.AddressBuilder()
.line1("Test Line 1")
.line2("Test Line 2")
.postCode(postCode)
.county(county)
.country(country)
.build();
// Now we can use JUnit's assert statements to verify that the object was created correctly
assertEquals("Test Line 1", testAddress.getLine1());
assertEquals("Test Line 2", testAddress.getLine2());
assertEquals(postCode, testAddress.getPostCode());
assertEquals(county, testAddress.getCounty());
assertEquals(country, testAddress.getCountry());
}
}
It was easy to get this, I personally don’t like the overuse of comments, for me this is duplicating what the code is telling me, but of course I could choose not to copy them. Again, the test names are not descriptive enough, ok for this example, but again, I can change them to suite. In this case I just took the code as is, in reality if using for a significant project, I would pick and choose what I bring in.
After the initial wow! I settled on the view that this is a great tool, but must be used in the right way. We are familar with IDE’s auto generating say getters and setters or maven templates . What I have used it for is an extension to that (and of course more).
If I wanted to create new Spring Boot service, in the past I would probably use Spring Initialiser website and or a mix of Spring Tutorials to cut and paste the boiler plate code. The advantage of AI assistant is I can do all this and more without leaving the IDE. I can get the boiler plate structure up and running fairly quickly to create say an API, allowing me to get to the core logic much quicker. This feels like a really positive step forward, who wants to keep repeating these mundane tasks to create essentially boiler plate or template code.
I was also able to use it like stack-overflow, simply asking it questions such as ‘Whats the URL for swagger?’ or pasting a partial stack trace.
Using features like auto-generating POJOs, tests and builders is a time saver, especially getting the tests as well, although I want to explore these in more depth.
I do feel like this is a new milestone in Software Engineering in terms of how coding will be in the future. I compare it maybe to like when high level languages came out, before that it was low level assembly specific to the hardware, C and its compiler introduced coding to a new generation at a much higher level. It feels like this is another jump if not now, certainly on the horizon, but for me its a tool than needs to be used in the right way. Using this as a ‘co-pilot’, then it’s going to be very useful, speed up development and get to business logic quicker. There is a danger that it may lead to a wave of poorly architected software, with people lured into quick results and not building for the long term.
Software Developers will still be the experts, but hopefully this removes some of the mundane tasks, leaving us to focus on clean architecture, scalability and that important step of translating requirements into working software, the tools may change, but the expertise will still be required. There are many ways to build a quality application, just as there are many ways to build a poor one, it comes down to experts to ensure the right approach is taken regardless of the tools used.
There is still lots of things to resolve and understand. I used this primarily to generate template code, but this is 2-way communication, I could just as easily pasted code into the chat for refactoring for example. This begs questions such as where does the code go and how is it used who owns it? (JetBrains discuss this in their blog post). I can see some companies being very concerned about security and IP/copyright, the problem is, it’s like Pandora’s box and it will be hard to separate the bits you want from the bits you don’t?.
I will continue to use at home for personal projects. I would take the view that any commercial usage must be discussed and approved prior to its use to prevent any possible breach of confidentiality or company policies.
Certainly an interesting new direction, will be good to see how it plays out over then next few years, but I would say its here to stay and down to us to define how we want to use it effectively.