Amazon CodeWhisperer el asistente de programación de Amazon

Estamos viviendo una explosión de la IA y la programación. Están entrando en juego numerosos actores y, entre ellos, no podía faltar Amazon para meter dentro de los servicios que provee un asistente para la programación que han llamado CodeWhisperer. En este artículo vamos a ver como podemos usarlo y que nos ofrece. Cabe destacar que está en período de vista previa y se espera que evolucione. Dentro de todo el ecosistema de productos, seguramente su rival más directo seria Github Copilot, que ya hemos visto en otros artículos.

0. Índice de contenidos.

  1. Introducción.
  2. Entorno.
  3. Creación del modelo.
  4. Creación del repositorio.
  5. Creación del servicio.
  6. Creación del controlador.
  7. Conclusiones.
  8. Referencias.

1. Introducción.

Amazon CodeWhisperer es una herramienta que nos ayuda a hacer pair programming basado en IA que genera sugerencias de código en tiempo real en nuestro IDE para ayudarnos a crear software.

CodeWhisperer Individual es de uso gratuito y solo necesitamos un inicio de sesión con AWS Builder.

La arquitectura de CodeWhisperer se basa en LLM (Large Language Model) entrenado con grandes cantidades de código.

2. Entorno.

El tutorial está escrito usando el siguiente entorno:

  • Hardware: Portátil Lenovo t480 (1.80 GHz intel i7-8550U, 32GB DDR4).
  • Sistema Operativo: Linux fedora 6.2.15-300.fc38.x86_64
  • IntelliJ IDEA 2023.1.2 (Ultimate Edition)

3. Creación del modelo.

Antes de comenzar, para usar CodeWhisperer se necesita:

El plug-in sería el siguiente:
aws toolkit plugin intellij

Una vez tenemos todo instalado siguiendo los pasos anteriores, veremos un panel donde podemos pausar o iniciar las sugerencias.

panel aws toolkit intellij

Al igual que pasaba con Github Copilot, CodeWhisperer no nos genera el fichero del proyecto. Vamos a seguir los mismos pasos que vimos en el tutorial de Github Copilot que es un proyecto generado con spring initializr.

Vamos a contar con las siguientes dependencias:

  • lombok
  • spring-boot-starter-data-jpa
  • h2
  • spring-boot-starter-web

Siendo todo parte de la última versión hasta la fecha de Spring Boot 3 (3.1.0)

El

Text
pom.xml

queda asi:


Text
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>3.1.0</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.example</groupId>
    <artifactId>codewhisperer</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>codewhisperer</name>
    <description>codewhisperer</description>
    <properties>
        <java.version>17</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>com.h2database</groupId>
            <artifactId>h2</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <excludes>
                        <exclude>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                        </exclude>
                    </excludes>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>

Al ser la primera entidad vamos a pedirle a CodeWhisperer que nos genere el código de la entidad.

Con

Text
// create a jpa entity class called Student with lombok annotations

Como podemos ver funciona como Github Copilot:

sugerencia code whisperer

Esa sugerencia es bastante comedida, nos pone mas sugerencias en comentarios que hace pensar que no sabe bien que es lombok. Tiene más sugerencias que nos da en una ventana como esta:

diferentes sugerencias code whisperer

Otro ejemplo de sugerencia sería este:


Text
import lombok.NoArgsConstructor;

import javax.persistence.*;

@Data
@NoArgsConstructor
@ALLArgsConstructor
@Builder
@Entity
@Table(name = "student")
public class Student {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(name
            = "first_name", nullable = false)
    private String firstName;
    @Column(name = "last_name", nullable = false)
    private String lastName;
    @Column(name = "email", nullable = false)
    private String email;
    @Column(name
            "phone", nullable = false)
    private String phone;
    @Column(name = "address", nullable = false)
    private String address;
    @Column(name = "city", nullable = false)
    private String city;
    @Column(name = "state", nullable = false)
    private String state;
    @Column(name = "zip", nullable = false)
    private String zip;

    @Column(name = "country", nullable = false)
    private String country;

    @Column(name = "dob", nullable = false)
    private String dob;

    @Column(name = "gender", nullable = false)
    private String gender;

    @Column(name = "nationality", nullable = false)
    private String nationality;

    @Column(name = "language", nullable = false)
    private String language;

    @Column(name = "ethnicity", nullable = false)
    private String ethnicity;

    @Column(name = "religion", nullable = false)
    private String religion;

    @Column(name = "school", nullable = false)
    private String school;

    @Column(name = "grade", nullable = false)
    private String grade;

    @Column(name = "major", nullable = false)
    private String major;

    @Column(name = "minor", nullable = false)
    private String minor;

    @Column(name = "gpa", nullable = false)
    private String gpa;

    @Column(name = "graduation_date", nullable = false)
    private String graduationDate;

    @Column(name = "resume", nullable = false)
    private String resume;

}

Aquí vemos que adolece del mismo problema que Github Copilot, que usa el API de persistencia de

Text
javax

y no la de

Text
jakarta

. Vamos a pedirle que nos genere el código con la API de Jakarta. Otro problema que tiene es que no deja que ningún campo sea nulo. Por último está usando palabras cortas como dob en lugar de dateOfBirth que sería más legible. Vamos a intentar que use

Text
jakarta
Text
// jpa entity class called Student with lombok annotations using jakarta

Text
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

import javax.persistence.*;

@Data
@NoArgsConstructor
@ALLArgsConstructor
@Builder
@Entity
@Table(name = "student")
public class Student {
    @Id
    @SequenceGenerator(
            name = "student_sequence",
            sequenceName = "student_sequence",
            allocationSize = 1
    )

    @GeneratedValue(
            strategy = GenerationType.SEQUENCE,
            generator = "student_sequence"
    )

    private Long studentId;
    private String firstName;
    private String lastName;

    @Column(
            name = "email_address",
            nullable = false
    )

    private String emailId;

    @Embedded
    private Guardian guardian;

}

Ahora vemos que la respuesta no es consistente con la anterior, no ha generado los mismos campos, porque seguramente no tenga claro que es un estudiante y no itera sobre lo anterior como si fuera una conversación. De hecho es un poco raro poner a una columna emailId el nombre de email_address. Otra cosa rara es la clase Guardian que no existe. Tampoco ha usado las anotaciones de

Text
jakarta

.

Si intentamos darle más pistas para indicar que estamos usando spring boot 3 con el siguiente prompt:

Text
// create a jpa entity class called Student with lombok annotations an id and a name for springboot 3

Nos genera lo siguiente:


Text
public class Student {
    private Long id;
    private String name;

    public Student() {

    }

    public Student(Long id, String name) {
        this.id = id;
        this.name = name;
    }

    public Long getId() {
        return id;
    }
    public void setId(Long id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }

}

Nos damos cuenta que ha omitido toda la parte de lombok y no ha puesto nada de spring. Parece que le hemos confundido mas que ayudado. En algunas ocasiones puede parecer que mas que ayudarnos a nosotros, somos nosotros los que tenemos que ayudarle a él.

El mejor resultado para esta entidad lo hemos conseguido con el siguiente prompt:

Text
// create a jpa entity class called Student with lombok annotations

Text
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@AllArgsConstructor
@NoArgsConstructor
@Entity
@Table(name = "student")
public class Student {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(name = "first_name")
    private String firstName;

    @Column(name = "last_name")
    private String lastName;

    @Column(name = "email")
    private String email;

}

Es la clase más comedida en cuanto a campos y anotaciones. Con unos pocos retoques quedaría como nosotros queremos.

Da la sensación que a medida que hemos ido avanzando nos ha ido conociendo algo más y va afinando resultados.

También, a medida que vamos usando este tipo de herramientas nos vamos dando cuenta que estos asistentes están para asistir y que puede que intentar que nos genere entidades desde cero, sea mucho.

Si queremos que nos vaya ayudando en lugar de ponerle un comentario, se empeña en crearnos un User y no un Student.

Sugerencia user

Lo dejamos así.


Text
package com.example.codewhisperer.model;

import jakarta.persistence.*;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@AllArgsConstructor
@NoArgsConstructor
@Entity
@Table(name = "student")
public class Student {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(name = "first_name")
    private String firstName;

    @Column(name = "last_name")
    private String lastName;

    @Column(name = "email")
    private String email;


}

4. Creación del repositorio.

Para el repositorio, hemos intentado que nos asista, pero las sugerencias que nos da no son muy buenas. En el StudentRepository nos sugiere que usemos la entidad User.

sugerencia 1 repositorio
sugerencia 2 repositorio


Text
import com.example.codewhisperer.model.User;

Ayudándole se puede conseguir que genere el repositorio. Si cambiamos la entidad de dominio por la nuestra mejora, pero por ejemplo no le pone los tipos al

Text
JpaRepository

que sería algo así como

Text
JpaRepository<Student, Long>

. Es como si no tuviera contexto y por eso nos sugiere primero poner un User y luego no nos da la sugerencia de usar

Text
Student

y

Text
Long

en el

Text
JpaRepository

.


Text
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

import com.example.codewhisperer.model.Student;

@Repository
public interface StudentRepository extends JpaRepository {

}

Con la interfaz vacía, nos sugiere métodos que no tienen mucho sentido, ya que por ejemplo nuestra entidad no tiene un campo email. Lo mismo pasa con otros campos, el único campo que tiene la entidad es el id.


Text
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

import com.example.codewhisperer.model.Student;

@Repository
public interface StudentRepository extends JpaRepository {
    Student findByEmail(String email);

    Student findByEmailAndPassword(String email, String password);

    Student findById(long id);

    Student findByEmailAndPasswordAndId(String email, String password, long id);

    Student findByEmailAndPasswordAndIdAndDeleted(String email, String password, long id, boolean deleted);

    Student findByEmailAndPasswordAndDeleted(String email, String password, boolean deleted);

    Student findByEmailAndDeleted(String email, boolean deleted);

    Student findByEmailAndIdAndDeleted(String email, long id, boolean deleted);

    Student findByEmailAndId(String email, long id);

    Student findByEmailAndDeletedAndId(String email, boolean deleted, long id);

    Student findByEmailAndDeletedAndIdAndPassword(String email, boolean deleted, long id, String password);

    Student findByEmailAndDeletedAndIdAndPasswordAndDeleted(String email, boolean deleted, long id, String password, boolean deleted2);
}

Lo dejamos así.


Text
package com.example.codewhisperer.repository;
import com.example.codewhisperer.model.Student;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

@Repository
public interface StudentRepository extends JpaRepository<Student, Long> {

}

5. Creación del servicio.

Para el servicio, pasa lo mismo que con el repositorio, empieza a sugerirnos el uso de

Text
List

y

Text
Optional

, que son muy comunes. Pero la tercera sugerencia es que usemos la entidad

Text
User

que no existe.


Text
import java.util.List;
import java.util.Optional;

import com.example.codewhisperer.model.User;

Al igual que con el repositorio, si le ayudamos un poco, vamos a conseguir que nos genere el servicio. Le ponemos el modelo correcto y nos genera el servicio, pero como antes, nos empieza a sugerir métodos que no pueden darse, ya que no existen en la entidad.


Text
import java.util.List;
import java.util.Optional;

import com.example.codewhisperer.model.Student;

public interface StudentService {

    List<Student> findAll();

    Optional<Student> findById(Long id);

    Student save(Student student);

    void deleteById(Long id);

    void deleteAll();

    List<Student> findByLastName(String lastName);

    List<Student> findByFirstName(String firstName);

    List<Student> findByLastNameAndFirstName(String lastName, String firstName);

    List<Student> findByLastNameOrFirstName(String lastName, String firstName);

    List<Student> findByLastNameAndFirstNameAndAge(String lastName, String firstName, Integer age);

    List<Student> findByLastNameAndFirstNameAndAgeOrAge(String lastName, String firstName, Integer age, Integer age2);

    List<Student> findByLastNameAndFirstNameAndAgeAndAgeOrAge(String lastName, String firstName, Integer age, Integer age2, Integer age3);

    List<Student> findByLastNameAndFirstNameAndAgeAndAgeOrAgeOrAge(String lastName, String firstName, Integer age, Integer age2, Integer age3, Integer age4);
}

Lo dejamos así.


Text
package com.example.codewhisperer.service;

import com.example.codewhisperer.model.Student;

import java.util.List;

public interface StudentService {
    List<Student> getAllStudents();
    Student getStudentById(Long id);
    Student createStudent(Student student);
    Student updateStudent(Student student);
    void deleteStudent(Long id);
}

Vamos con la implementación.


Text
import com.example.codewhisperer.model.Student;
import org.springframework.stereotype.Service;

@Service
public class StudentServiceImpl {

    public Student getStudent(String id) {
        return new Student(id, "John", "Doe");
    }

    public Student getStudent(String id, String firstName, String lastName) {
        return new Student(id, firstName, lastName);
    }

    public Student getStudent(String id, String firstName, String lastName, String email) {
        return new Student(id, firstName, lastName, email);
    }

    public Student getStudent(String id, String firstName, String lastName, String email, String phone) {
        return new Student(id, firstName, lastName, email, phone);
    }

    public Student getStudent(String id, String firstName, String lastName, String email, String phone, String address) {
        return new Student(id, firstName, lastName, email, phone, address);
    }

    public Student getStudent(String id, String firstName, String lastName, String email, String phone, String address, String city) {
        return new Student(id, firstName, lastName, email, phone, address, city);
    }
}

Dándole otro pequeño empujón, le indicamos que es lo que queremos (que nos implemente el StudentService) y ya si nos va ofreciendo más ayuda. Aun así, necesitamos dejarle casi todo hecho.


Text
import com.example.codewhisperer.model.Student;
import com.example.codewhisperer.model.repository.StudentRepository;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.Optional;

@Service
public class StudentServiceImpl implements StudentService {

    private final StudentRepository studentRepository;

    public StudentServiceImpl(StudentRepository studentRepository) {
        this.studentRepository = studentRepository;
    }

    @Override
    public List<Student> findAll() {
        return studentRepository.findAll();
    }

    @Override
    public Optional<Student> findById(Long id) {
        return studentRepository.findById(id);

    }

    @Override
    public Student save(Student student) {
        return studentRepository.save(student);
    }

    @Override
    public void deleteById(Long id) {
        studentRepository.deleteById(id);
    }

    @Override
    public void deleteAll() {
        studentRepository.deleteAll();
    }
}

Como vemos a continuación, muchas de las sugerencias tienen código de más o nos da tests un poco dudosos. Hay que ir jugando y tener paciencia con las recomendaciones.

sugerencia 1 servicio
sugerencia 1 servicio
sugerencia 2 servicio
sugerencia 2 servicio

Queda así


Text
package com.example.codewhisperer.service;

import com.example.codewhisperer.model.Student;
import com.example.codewhisperer.repository.StudentRepository;
import org.springframework.stereotype.Service;

import java.util.List;

@Service
public class StudentServiceImpl implements StudentService {

    private StudentRepository studentRepository;

    public StudentServiceImpl(StudentRepository studentRepository) {
        this.studentRepository = studentRepository;
    }

    @Override
    public List<Student> getAllStudents() {
        return studentRepository.findAll();
    }

    @Override
    public Student getStudentById(Long id) {
        return studentRepository.findById(id).orElseThrow(() -> new RuntimeException("Student not found"));
    }

    @Override
    public Student createStudent(Student student) {
        return studentRepository.save(student);
    }

    @Override
    public Student updateStudent(Student student) {
        return studentRepository.save(student);
    }

    @Override
    public void deleteStudent(Long id) {
        studentRepository.deleteById(id);
    }
}

6. Creación del controlador.

En el caso del controlador, nos ayuda más. El path que nos sugiere esta bastante bien y a medida que vamos implementando la clase, nos va sugiriendo métodos. Da la impresión que los controladores los genera mejor, tal vez porque son más sencillos y sus implementaciones suelen ser siempre más o menos iguales. En cambio en servicios y repositorios se puede meter lógica de negocio.


Text
import com.example.codewhisperer.service.StudentService;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/api/v1/students")
public class StudentController {

    private final StudentService studentService;

    public StudentController(StudentService studentService) {
        this.studentService = studentService;
    }

    @GetMapping
    public String getStudents() {
        return studentService.getStudents();
    }
}

Pero tenemos que ir corrigiendo los tipos que devuelve. También vemos que se lía con el nombre de los métodos. Lo dejamos solo con el get


Text
package com.example.codewhisperer.controller;

import com.example.codewhisperer.model.Student;
import com.example.codewhisperer.service.StudentService;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

@RestController
@RequestMapping("/api/v1/students")
public class StudentController {

    private final StudentService studentService;

    public StudentController(StudentService studentService) {
        this.studentService = studentService;
    }

    @GetMapping
    public List<Student> getStudents() {
        return studentService.getAllStudents();
    }
}

A medida que hemos ido implementando cosas, ha ido metiendo endpoints que apuntan a tests. Parece que está mezclando la implementación con los tests, pero no ha sugerido importar nada relacionado con los tests.


Text
@GetMapping("/test")
    public String test() {
        return "test";

    }

7. Conclusiones.

Todos los ejemplos que hemos visto, al igual que anteriores artículos, no son deterministas, por lo que siguiendo los mismos pasos, se pueden llegar a situaciones distintas.

Basándonos en lo que hemos probado de Amazon CodeWhisperer, creemos que tiene potencial, pero mucho camino por recorrer. Parece que está un paso por detrás de Github Copilot, pero estando Amazon detrás, seguro que consigue ponerse a la altura.

Tras las pruebas que se han hecho con CodeWhisperer y Copilot, da la sensación que no son muy buenos de momento a la hora de crear cosas desde cero, o que cuando hay muchas posibilidades, no dan muy buenos resultados. Por ejemplo, un controlador siempre suele ser una llamada al servicio. En cambio, con los servicios, donde seguramente ha visto código con más lógica, no ha sido muy útil.

Los ejemplos que siempre nos ponen, son ejemplos de algoritmos, donde ellos tienen ventaja, ya que si es un problema matemático conocido, es más fácil que ellos sepan la solución óptima, mientras que nosotros podemos no ser conscientes de que hay varias alternativas o incluso no saber que ese problema tiene una solución conocida y hacer nosotros una solución que no sea la más óptima o legible.

Si vemos la solución de geeksforgeeks:


Text
// Java program for implementation of QuickSort
class QuickSort
{
        /* This function takes last element as pivot,
        places the pivot element at its correct
        position in sorted array, and places all
        smaller (smaller than pivot) to left of
        pivot and all greater elements to right
        of pivot */
        int partition(int arr[], int low, int high)
        {
                int pivot = arr[high];
                int i = (low-1); // index of smaller element
                for (int j=low; j<high; j++)
                {
                        // If current element is smaller than or
                        // equal to pivot
                        if (arr[j] <= pivot)
                        {
                                i++;

                                // swap arr[i] and arr[j]
                                int temp = arr[i];
                                arr[i] = arr[j];
                                arr[j] = temp;
                        }
                }

                // swap arr[i+1] and arr[high] (or pivot)
                int temp = arr[i+1];
                arr[i+1] = arr[high];
                arr[high] = temp;

                return i+1;
        }


        /* The main function that implements QuickSort()
        arr[] --> Array to be sorted,
        low --> Starting index,
        high --> Ending index */
        void sort(int arr[], int low, int high)
        {
                if (low < high)
                {
                        /* pi is partitioning index, arr[pi] is
                        now at right place */
                        int pi = partition(arr, low, high);

                        // Recursively sort elements before
                        // partition and after partition
                        sort(arr, low, pi-1);
                        sort(arr, pi+1, high);
                }
        }

        /* A utility function to print array of size n */
        static void printArray(int arr[])
        {
                int n = arr.length;
                for (int i=0; i<n; ++i)
                        System.out.print(arr[i]+" ");
                System.out.println();
        }

        // Driver program
        public static void main(String args[])
        {
                int arr[] = {10, 7, 8, 9, 1, 5};
                int n = arr.length;

                QuickSort ob = new QuickSort();
                ob.sort(arr, 0, n-1);

                System.out.println("sorted array");
                printArray(arr);
        }
}
/*This code is contributed by Rajat Mishra */

La respuesta de codewhisperer es más rebuscada. Si optamos por la versión estática con clase, acabamos en un callejón sin salida

Quicksort estático
Quicksort estático
Implementación quicksort estático
Implementación quicksort estático

Si optamos por la versión estática de la función sin clase (la primera sugerencia que nos da), este es el código


Text
public static void main(String[] args) {
        final int[] arr = {5, 9, 4, 6, 5, 3};
        // Sort the array using quick sort
        quickSort(arr, 0, arr.length - 1);
        // Print the sorted array
        for (int i = 0; i < arr.length; i++) {
            System.out.print(arr[i] + " ");
        }
    }

    private static void quickSort(int[] arr, int i, int i1) {
        int pivot = arr[i];
        int low = i;
        int high = i1;

        while (low < high) {
            while (arr[high] >= pivot && low < high) {
                high--;
            }
            while (arr[low] <= pivot && low < high) {
                low++;
            }
            if (low < high) {
                int temp = arr[low];
                arr[low] = arr[high];
                arr[high] = temp;
            }
        }
        arr[i] = arr[low];
        arr[low] = pivot;
        if (i < low - 1) {
            quickSort(arr, i, low - 1);
        }
        if (i1 > low + 1) {
            quickSort(arr, low + 1, i1);
        }
        System.out.println(arr);
        System.out.println(low);
        System.out.println(high);
        System.out.println(i);
        System.out.println(i1);
    }

Hemos tenido que crear la clase y la función desde el IDE porque no nos da una respuesta entera.

Puede que cuando esto avance, en vez de pasarnos tiempo pensando en soluciones, pasemos tiempo pensando en mejores soluciones y haciendo software de mayor calidad o más óptimo.

Esto no ha hecho más que empezar, hay muchas empresas con muchos intereses que están apostando fuerte en estas tecnologías, así que veremos un avance exponencial y, donde ahora vemos que le tenemos que ayudar más, nos ayudara más él a nosotros.

También hay que tener cuidado con la privacidad de estas herramientas en general.

8. Referencias.

Comentarios

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

He leído y acepto la política de privacidad

Información básica acerca de la protección de datos

  • Responsable: IZERTIS S.A.
  • Finalidad: Envío información de carácter administrativa, técnica, organizativa y/o comercial sobre los productos y servicios sobre los que se nos consulta.
  • Legitimación: Consentimiento del interesado
  • Destinatarios: Otras empresas del Grupo IZERTIS. Encargados del tratamiento.
  • Derechos: Acceso, rectificación, supresión, cancelación, limitación y portabilidad de los datos.
  • Más información: Puedes ampliar información acerca de la protección de datos en el siguiente enlace:política de privacidad

Ingeniero técnico en informática de sistemas y graduado en ingeniería de software en la UPM. Master en cloud apps en la URJC. De lunes a viernes intento escribir código aburrido, simple y sistemático. Los fines de semana me suelto la melena, experimento y hasta uso lenguajes no tipados.

¿Quieres publicar en Adictos al trabajo?

Te puede interesar

02/03/2026

José Antonio Sánchez Segovia

Zephyr es un RTOS open source respaldado por la Linux Foundation que permite desarrollar dispositivos embebidos conectados, eficientes y escalables, facilitando el paso de prototipo a producto final con una arquitectura mantenible.

23/02/2026

Enrique Casado Díez

LoRa y LoRaWAN son tecnologías clave en el ecosistema IoT cuando se requiere largo alcance y bajo consumo energético. En este artículo analizamos su funcionamiento, Spreading Factor, link budget, arquitectura de red, frecuencias y clases de dispositivos, con un caso práctico real.

19/02/2026

Juan José Díaz Antuña

Copilot Chat es la forma más sencilla y segura de empezar a usar IA en Microsoft 365. En este artículo vemos cómo funciona, cómo activarlo y en qué se diferencia de Microsoft 365 Copilot, Copilot Studio y los Agentes Inteligentes, con ejemplos prácticos y una comparativa clara.