Going deeper using CodeGPT

Enhance your code using AI

Luis Diaz
Globant
8 min readSep 1, 2023

--

In the previous article, we talked about the usefulness of CodeGPT; we found it to be awesome. Now, let’s focus on its “Optimize”, “Refactor”, “Explain”, “Write Test”, and “Find bugs” features. To understand their power, I will revisit an old project, which lacked clean architecture, SOLID principles, and code quality.

Let’s Begin!

Let’s test if the “Optimize and Refactor” features can improve this login code.

<?xml version="1.0" encoding="utf-8"?>

<ScrollView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:background="@drawable/fondo_sin"
android:layout_width="match_parent"
android:layout_height="match_parent">

<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">

<ImageView android:id="@+id/vaca"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
android:layout_width="match_parent"
android:layout_height="@dimen/sign_fondo"
android:src="@drawable/ic_launcher_background"/>

<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/recuadro"
android:layout_width="match_parent"
android:layout_height="@dimen/loginh"
app:layout_constraintTop_toBottomOf="@id/vaca"
android:layout_marginStart="@dimen/separa3"
android:layout_marginEnd="@dimen/separa3"
android:background="@drawable/recuadro">

<ImageView android:id="@+id/imgregistro"
android:layout_width="@dimen/datos"
android:layout_height="@dimen/datos"
android:src="@drawable/ic_user"
app:layout_constraintEnd_toStartOf="@id/registro"
app:layout_constraintTop_toTopOf="@id/registro"
app:layout_constraintBottom_toBottomOf="@id/registro"/>

<TextView android:id="@+id/registro"
android:text="@string/usuario"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintStart_toStartOf="@id/recuadro"
app:layout_constraintEnd_toEndOf="@id/recuadro"
app:layout_constraintTop_toTopOf="@id/recuadro"
android:layout_marginTop="@dimen/separa"
android:textSize="@dimen/text25"
android:textColor="@color/black"
android:background="@drawable/recuadro" />

<EditText android:id="@+id/etxCorreo"
android:layout_width="match_parent"
android:layout_height="@dimen/cuadroh_login"
app:layout_constraintTop_toBottomOf="@id/registro"
app:layout_constraintStart_toStartOf="@id/recuadro"
app:layout_constraintEnd_toEndOf="@id/recuadro"
android:layout_marginTop="@dimen/separa"
android:layout_marginStart="@dimen/separa"
android:layout_marginEnd="@dimen/separa"
style="@style/datos.user"
android:inputType="textEmailAddress"/>

<ImageView android:id="@+id/imgregistro2"
android:layout_width="@dimen/datos"
android:layout_height="@dimen/datos"
android:src="@drawable/ic_pass"
app:layout_constraintEnd_toStartOf="@id/registro2"
app:layout_constraintTop_toTopOf="@id/registro2"
app:layout_constraintBottom_toBottomOf="@id/registro2" />

<TextView android:id="@+id/registro2"
android:text="@string/password"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintTop_toBottomOf="@id/etxCorreo"
app:layout_constraintStart_toStartOf="@id/registro"
app:layout_constraintEnd_toEndOf="@id/registro"
android:layout_marginTop="@dimen/separa"
android:textSize="@dimen/text25"
android:textColor="@color/black"
android:background="@drawable/recuadro"/>

<EditText android:id="@+id/etxPass"
android:layout_width="match_parent"
android:layout_height="@dimen/cuadroh_login"
app:layout_constraintTop_toBottomOf="@id/registro2"
app:layout_constraintStart_toStartOf="@id/etxCorreo"
app:layout_constraintEnd_toEndOf="@id/etxCorreo"
android:layout_marginTop="@dimen/separa"
android:layout_marginStart="@dimen/separa"
android:layout_marginEnd="@dimen/separa"
style="@style/datos.user"
android:inputType="textPassword"
android:imeOptions="actionDone"/>

</androidx.constraintlayout.widget.ConstraintLayout>

<!--
<TextView android:id="@+id/btnRegistrarse"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintTop_toBottomOf="@id/recuadro"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
android:layout_marginTop="@dimen/separa2"
android:text="@string/registrar"
android:textSize="@dimen/text1"
android:textColor="@color/bluef" /> -->

<androidx.appcompat.widget.AppCompatButton
android:id="@+id/btnlogin"
android:layout_width="@dimen/ancho_btn"
android:layout_height="wrap_content"
app:layout_constraintTop_toBottomOf="@id/recuadro"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
android:text="@string/iniciar_btn"
android:textSize="@dimen/text20"
android:layout_marginTop="@dimen/separa5"
style="@style/blue.button" />

<TextView android:id="@+id/btnAcceder"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintTop_toBottomOf="@id/btnlogin"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
android:layout_marginTop="@dimen/separa2"
android:text="@string/acceder"
android:textSize="@dimen/text20"
android:textColor="@color/bluef"/>

<ImageButton android:id="@+id/btnFb"
android:layout_width="@dimen/botones"
android:layout_height="@dimen/botones"
app:layout_constraintStart_toStartOf="@id/btnAcceder"
app:layout_constraintTop_toBottomOf="@id/btnAcceder"
android:layout_marginTop="@dimen/separa2"
android:layout_marginStart="@dimen/separa3"
android:src="@mipmap/ic_launcher_fb"/>

<ImageButton android:id="@+id/btnGoogle"
android:layout_width="@dimen/botones"
android:layout_height="@dimen/botones"
app:layout_constraintEnd_toEndOf="@id/btnAcceder"
app:layout_constraintTop_toBottomOf="@id/btnAcceder"
android:layout_marginTop="@dimen/separa2"
android:src="@mipmap/ic_launcher_google"
android:layout_marginEnd="@dimen/separa3" />

<ProgressBar
android:id="@+id/progressBar2"
style="?android:attr/progressBarStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
android:visibility="gone" />
</androidx.constraintlayout.widget.ConstraintLayout>
</ScrollView>

I copy and paste the old code, intentionally adding bad code practices to test the plugin’s abilities. I also include unnecessary code like redrawing the background and extra empty lines that are not needed.

So now, let’s see what happens if we select all the code and test the “Optimize” feature.

Selecting Optimize option on CodeGPT
Selecting the Optimize option on CodeGPT

The result is a little disappointing. It looks like CodeGPT was not able to help us as we used to, only giving us some suggestions to follow and take care of.

Result of “Optimize” option
Result of “Optimize” option

We need to check if these concepts are valid in our code. Maybe, we are expecting possible issues and should fix them immediately by the plugin. We can test this in a Kotlin file, but we’ll do it later. For now, let’s continue exploring the options that CodeGPT offers for XML files. So now let’s try the “Refactor” option.

Result of “Refactor” option
Result of “Refactor” option

Like the “Optimize” option, we have only some suggestions to take in mind. Let’s see if we can have some exciting results on the other options by trying the “Explain” option.

Result of “Explain” option
Result of “Explain” option

The result is better than the others. It gives us a general explanation of our screen. There is a view calledBtnRegistrarse, and ChatGPT suggests removing the comment to see theView. Now let’s see if it can create some tests; maybe we can get some automated UI tests, let’s see.

Result of “Write Tests” option
Result of the “Write Tests” option

This result is good. CodeGPT doesn’t know business rules, so it can’t do much here. We’ll test it later. Now, let’s look at the last one “Find Bugs”. In the previous code, there were no bugs like this, but there are some opportunities for improvement. We will see how this behaves on the XML file.

Result of “Find Bugs” option
Result of “Find Bugs” option

CodeGTP is having trouble finding resources like dimensions, colors, and images. CodeGPT suggests “code shared,” but I didn’t understand what that meant. Eventually, I realized it couldn’t find those references. Keep experimenting with the XML. Try copying and pasting the same view with the same ID. Write something incorrectly, like “Textviewkasjdi.” CodeGPT won’t recognize it as an error but may offer suggestions.

As an Android developer, I don’t find this test very helpful for XML files, so I’m not happy with it. The plugin should recognize when there is a double ID for a View or if there is an invalid View from the Android library. It will let you know if something is wrong.

Testing Options on Kotlin Code!

OK, now switch to the Kotlin environment. To keep the same line let’s do the test under the Login Screencontext. Here is the code:

class LogInActivity: AppCompatActivity(){
private lateinit var binding: ActivityLogInBinding
private val model: LoginViewModel by lazy {
getViewModel { LoginViewModel(UserInfoRepository(ServiceApi().getClienteApi())) }
}

override fun onCreate(savedInstanceState: Bundle?){
super.onCreate(savedInstanceState)
binding = ActivityLogInBinding.inflate(layoutInflater)
setContentView(binding.root)

//paso a pantalla Registro
binding.btnAcceder.setOnClickListener {
val i = Intent(applicationContext, RegistroActivity::class.java)
startActivity(i)
}

initEnter()
}


//Valua que todos los campos estén completos para acceder con el usuario
private fun initEnter() {
binding.btnlogin.setOnClickListener {
val correo = valCorreo()
val pass = valPass()

if (correo && pass){
loginCliente()
} else{
Snackbar.make(binding.root, "Completa todos los campos" , Snackbar.LENGTH_LONG)
.setAction("Action", null).show()
}
}
}


//Valua que todos los campos estén llenos
private fun loginCliente() {
model.loginUsuario(
binding.etxCorreo.text.toString(),
binding.etxPass.text.toString()
)
initObservers()
}


//Observer
private fun initObservers() {
model.networkState.observe(this) {
when (it.status) {
UtilsNetwork.SUCCESS, UtilsNetwork.FAILED -> {
binding.btnlogin.isEnabled = true
binding.progressBar2.visibility = View.GONE
}
UtilsNetwork.FAILED -> {
binding.btnlogin.isEnabled = true
binding.progressBar2.visibility = View.GONE
}
UtilsNetwork.RUNNING -> {
binding.btnlogin.isEnabled = false
binding.progressBar2.visibility = View.VISIBLE
}
}
}
//Respuesta ERROR y respuesta CORRECTA tras mandar los datos de usuario
model.badResponse.observe(this) { response ->
Snackbar.make(binding.root, response.message , Snackbar.LENGTH_LONG)
.setAction("Action", null).show()
}
model.godResponse.observe(this){
Snackbar.make(binding.root, "Bienvenido " + it.name , Snackbar.LENGTH_LONG)
.setAction("Action", null).show()


//Guarda ciertos datos para después
val pref = applicationContext.getSharedPreferences ("MyPref", 0)
val editor = pref.edit()
editor.putString("nombre", it.name)
editor.putString("id", it.id_user)
editor.putString("tipo", it.tipo)
editor.putString("correo", it.correo)


editor.commit()

val j = Intent(applicationContext, MainActivity::class.java)
startActivity(j)
finish()
}

}


//Evalúa cada campo para verificar que no esté vacío
fun valPass(): Boolean {
if (binding.etxPass.text.toString() == ""){
binding.etxPass.error = "Ingresa una contraseña"
return false
} else{
return true
}
}

fun valCorreo(): Boolean {
if (binding.etxCorreo.text.isEmpty()) {
binding.etxCorreo.error = "Ingresa tu correo"
return false
} else {
return true
}
}
}

The code wasn’t too bad, but I made some updates to fix errors or warnings in the XML. Let’s see if we get better results. Again let’s start with the first option “Optimize”.

Result of “Optimize” option

We have some useful suggestions. Remove unused imports and extra empty lines. In the previous code snippet, there is an extra case in the switch statement. CodeGTP has fixed this issue. We have also improved the conditionals for empty fields.

I can see that it’s trying to use Coroutines, which is great. But in this project, it is not using it anywhere. This is because there’s missing context in the other files and layers. Another thing is that instead of using commit() for shared preferences, it recommends using apply(). Lastly, I noticed they added an extra condition for the email field, which is a good point. I see some ways to make the snack bar more efficient, like showing or hiding view elements based on the response. It could be more descriptive by using enableScreenState and disableScreenState. This is just a suggestion for developers.

Now we’ll move on to the next option and see the analysis results without changing the code. So let’s figure out what should be the difference between “Optimize and Refactor”.

Result of “Refactor” option
Result of “Refactor” option

In my opinion, the best result for this code should be a combination of those 2 suggestions. The apply scope function is a good idea on SharedPreferences, for instance. Once you’re done coding, it’s a good idea to test a few options and choose the best one based on CodeGPT’s suggestions.

OK, then let’s try the “Explain” option on the base code.

Result of “Explain” option
Result of “Explain” option

This result was very good; a quick understanding overall of this new code for any new developer would be very helpful. Just in a few seconds, we have a general idea about where is the initial point to send data to API, how the response should be handled, and also the function validations.

Now, let’s move on to the “Find Bug” option. Currently, the base code is bug-free. Let’s see what CodeGPT can find.

Result of “Find Bug” option
Result of “Find Bug” option

It found no bugs, but we have suggestions to improve the user experience. Suggestions include advance validations, navigation experience, and code improvement.

So now the final option and the one that in my previous article I did mention, and I loved it. The “Write Tests” option. Right now, our code is a little more complex than in the last article. I hope this tool can show a good contrast.

Result of “Write Tests” option
Result of “Write Tests” option

So CodeGPT gave us 3 Unit Tests using Mockito and Espresso; validation of 2 EditText before click on Login Button, what should happen after pressing the Login Button and how we should handle the response from the service call. I’m sure that if for any reason in your project only is implemented Mockito you can ask to CodeGPT re-write those Unit Tests without problem, so in just a few seconds, we have all the necessary Unit Tests for this simple code.

CodeGPT provided us with 3 Unit Tests. These tests use Mockito and Espresso. The first test checks if 2 EditText fields are validated before clicking on the Login Button. The second test verifies what should happen after pressing the Login Button. The third test focuses on how we should handle the response from the service call. If you only use Mockito for your project, you can easily ask CodeGPT to rewrite the Unit Tests. In just a few seconds, you’ll have all the necessary Unit Tests for this simple code.

Should we continue using CodeGPT ?

My recommendation about how to use CodeGPT should be, after you finish coding and of course to grow technical and professionally, try to improve your code, and when you don’t see any other improvement try the “Optimize” option; maybe you missed a repetitive task, or an unused import, etc. but as always we recommend; you have to check before implementing all the code suggested. Also, Unit Tests are always welcome for every code written, which helps us to have a good healthy project.

I loved two options in this plugin: “Optimize” and “Write Tests”. These could be helpful to me every day. The other options are also great, especially for new projects. They can reduce the learning curve.

--

--