Issue 1, automate your developer workflows

Issue 1, automate your developer workflows

This newsletter is really directed at anyone in the age of AI assistant. My hope is that you by reading this newsletter, you get ideas on how to use AI assistants to automate your work and make your life easier.

What a developer does

Being a developer means you do a lot more things than just writing code. Things you do are usually something like so:

  • Communicate with stakeholders, a crucial part of your day job is finding out what to build.
  • Write code, this is the fun part, and perhaps what many think developers do all day but we're lucky to get to do this 50% of the time.
  • Write tests, this is the not so fun part, but crucial to ensure that your code works.
  • Review code, this is often your last line of defense before code is deployed to production.
  • Deploy code, this usually doesn't involve code writing but if you're lucky you get to write some scripts to automate this, so it's not manual.
  • Monitor code. Usually, asa developer, this isn't your main concern, but you might get pinged if something goes wrong.
  • Document code. Yes, you should do this somehow, no code isn't self-documenting.

In a larger company

In a larger company you might have the luxury of doing a smaller subset of the above like:

  • Communicate with stakeholders, this is still an important piece, maybe even more important in a large company.
  • Write code, that's what you do mostly.
  • Write tests, large companies care even more about tests in my experience.
  • Document code.

as the other tasks are handled by other teams.

Ok, so let's say you're only working on these four tasks above, how can an AI tool help me? Well, there are usually two major types of projects you're working on:

  • "greenfield", new projects
  • "brownfield", existing projects.

Depending on the type, you would use an AI tool to solve different tasks.

Greenfield projects

In a greenfield project, you start with a blank sheet, so how can I get help? If you're a web developer, many times the job is about building a Web API. Of course, saying that, there's a lot of assumed context what that means, there are concepts like REST, routes, controllers, models, query parameters, headers and so on.

Example - building a Web API using AI

You've done this task many many times before manually, so let's do it again, but this time using an AI tool.

First, let's create a prompt for the AI tool to use (use Chat GPT or GitHub Copilot Chat for this one):

Create a Web API that has routes customers, products and orders, should support HTTP verbs GET, POST, PUT, DELETE and use Python and Flask.        

The above prompt does two things:

  • It gives direction on API creation like what domain concepts like customers, products, and orders.
  • Secondly, it specifies the technology stack to use, Python and Flask. (We'll change this later so you can see that this prompt can be easily customized)

Here's the outcome of the prompt:

from flask import Flask, request, jsonify
from flask_sqlalchemy import SQLAlchemy

app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:////tmp/test.db'
db = SQLAlchemy(app)

class Customer(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(50), nullable=False)

class Product(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(50), nullable=False)

class Order(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    customer_id = db.Column(db.Integer, db.ForeignKey('customer.id'), nullable=False)
    product_id = db.Column(db.Integer, db.ForeignKey('product.id'), nullable=False)

@app.route('/customers', methods=['GET', 'POST'])
def handle_customers():
    if request.method == 'POST':
        if not request.is_json:
            return jsonify({"msg": "Missing JSON in request"}), 400
        name = request.json.get('name', None)
        if not name:
            return jsonify({"msg": "Missing name parameter"}), 400
        customer = Customer(name=name)
        db.session.add(customer)
        db.session.commit()
        return jsonify({"msg": "Customer created", "customer": name}), 201
    customers = Customer.query.all()
    return jsonify([customer.name for customer in customers])

@app.route('/customers/<int:id>', methods=['GET', 'PUT', 'DELETE'])
def handle_customer(id):
    customer = Customer.query.get(id)
    if not customer:
        return jsonify({"msg": "Customer not found"}), 404
    if request.method == 'PUT':
        if not request.is_json:
            return jsonify({"msg": "Missing JSON in request"}), 400
        name = request.json.get('name', None)
        if not name:
            return jsonify({"msg": "Missing name parameter"}), 400
        customer.name = name
        db.session.commit()
        return jsonify({"msg": "Customer updated", "customer": name}), 200
    if request.method == 'DELETE':
        db.session.delete(customer)
        db.session.commit()
        return jsonify({"msg": "Customer deleted"}), 200
    return jsonify({"customer": customer.name})        

That is quite an interesting response, we actually got more than we asked for as it gives us the following:

  • A database schema, we didn't ask for this, but it's a nice bonus.
  • A Flask application, we asked for this.
  • A set of routes and handlers for customers, products, and orders, we asked for this.
  • A set of HTTP verbs for each route, we asked for this.

Improving the code

Of course, at this point, the code is not perfect, but it's a good starting point. You can keep working on this code and improve it either manually or using an AI tool, let's do the latter by crafting a new prompt (here's where your knowledge as a dev comes in, you're the pilot, it's the copilot, you're in control).

Type this prompt to improve the code (yes, your AI assistant usually doesn't need more than that for context):

improve the above code          

That's not a lot to go on, but let's see what we get, improve "how" you might ask yourself:

First you get a text response that explains what the AI assistant thinks you want to improve:

The provided code is a good starting point for a Flask API. However, there are several improvements that can be made to make the code more robust and maintainable:

1. **Separate the models into a different file**: This will make the code more organized and easier to maintain.

2. **Use Marshmallow for Serialization**: Marshmallow is a popular library for object serialization/deserialization. It can be used to validate input data and serialize output data.

3. **Use Flask-Restful**: Flask-Restful is an extension for Flask that adds support for quickly building REST APIs. It encourages best practices with minimal setup.

4. **Error Handling**: Add error handling to return meaningful error messages to the client.

Here's how the improved code might look:        

and then comes the code:

# models.py
from flask_sqlalchemy import SQLAlchemy

db = SQLAlchemy()

class Customer(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(50), nullable=False)

class Product(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(50), nullable=False)

class Order(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    customer_id = db.Column(db.Integer, db.ForeignKey('customer.id'), nullable=False)
    product_id = db.Column(db.Integer, db.ForeignKey('product.id'), nullable=False)

# app.py
from flask import Flask
from flask_restful import Api, Resource
from models import db, Customer, Product, Order

app = Flask(__name__)
api = Api(app)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:////tmp/test.db'
db.init_app(app)

class CustomerResource(Resource):
    def get(self, id=None):
        if id:
            customer = Customer.query.get(id)
            if not customer:
                return {"error": "Customer not found"}, 404
            return customer.json()
        customers = Customer.query.all()
        return [customer.json() for customer in customers], 200

    # Implement POST, PUT, DELETE similarly

api.add_resource(CustomerResource, '/customers', '/customers/<int:id>')

# Similar resources would be created for 'products' and 'orders'        

This looks like a lot less code, but it's

  • Using a library to handle a resource, which deals with all HTTP verbs, so less to write.
  • It's also leaving you to do some implementation yourself as it tells you to "Implement POST, PUT, DELETE similarly".

At this point, you could really go in any direction, like for example:

  • tell it to give you the full code
  • ask it to write tests
  • ask it to solve the problem differently, i.e. without the flask_restful library

and above is really the beauty of it, it's like driving a car, you're in control, you can keep going in the same direction, or take a turn, or stop, or go back, it's up to you.


Fixing the output - give me the missing code

But let's try to actually see if it's able to complete the code for us, by trying this prompt:

you're missing code, please add missing implementations to include all resources and all HTTP verbs        

As a developer, I can tell what's missing, let's see if the AI assistant can figure out what I need it to do:

Here's the outcome:

Sure, I'll add the missing implementations for the Product and Order resources. Here's how you could implement these resources using Flask-Restful:        

Ok, sounds promising, let's see the code:

# models.py
from flask_sqlalchemy import SQLAlchemy

db = SQLAlchemy()

class Customer(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(50), nullable=False)

class Product(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(50), nullable=False)

class Order(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    customer_id = db.Column(db.Integer, db.ForeignKey('customer.id'), nullable=False)
    product_id = db.Column(db.Integer, db.ForeignKey('product.id'), nullable=False)

# app.py
from flask import Flask
from flask_restful import Api, Resource
from models import db, Customer, Product, Order

app = Flask(__name__)
api = Api(app)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:////tmp/test.db'
db.init_app(app)

class CustomerResource(Resource):
    def get(self, id=None):
        if id:
            customer = Customer.query.get(id)
            if not customer:
                return {"error": "Customer not found"}, 404
            return customer.json()
        customers = Customer.query.all()
        return [customer.json() for customer in customers], 200

    # Implement POST, PUT, DELETE similarly

api.add_resource(CustomerResource, '/customers', '/customers/<int:id>')

class ProductResource(Resource):
    def get(self, id=None):
        if id:
            product = Product.query.get(id)
            if not product:
                return {"error": "Product not found"}, 404
            return product.json()
        products = Product.query.all()
        return [product.json() for product in products], 200

    # Implement POST, PUT, DELETE similarly

api.add_resource(ProductResource, '/products', '/products/<int:id>')

class OrderResource(Resource):
    def get(self, id=None):
        if id:
            order = Order.query.get(id)
            if not order:
                return {"error": "Order not found"}, 404
            return order.json()
        orders = Order.query.all()
        return [order.json() for order in orders], 200

    # Implement POST, PUT, DELETE similarly

api.add_resource(OrderResource, '/orders', '/orders/<int:id>')        

Ok, so I got the missing resource implementation, but it's still missing the HTTP verbs, let's see if it can figure that out, with another prompt:

thanks, better, but you are still missing HTTP verb implementations, please add that        

Ok, let's see what we get:

# app.py
from flask import Flask, request
from flask_restful import Api, Resource
from models import db, Customer, Product, Order

app = Flask(__name__)
api = Api(app)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:////tmp/test.db'
db.init_app(app)

class CustomerResource(Resource):
    def get(self, id=None):
        if id:
            customer = Customer.query.get(id)
            if not customer:
                return {"error": "Customer not found"}, 404
            return customer.json()
        customers = Customer.query.all()
        return [customer.json() for customer in customers], 200

    def post(self):
        name = request.json.get('name')
        new_customer = Customer(name=name)
        db.session.add(new_customer)
        db.session.commit()
        return new_customer.json(), 201

    def put(self, id):
        customer = Customer.query.get(id)
        if not customer:
            return {"error": "Customer not found"}, 404
        customer.name = request.json.get('name')
        db.session.commit()
        return customer.json(), 200

    def delete(self, id):
        customer = Customer.query.get(id)
        if not customer:
            return {"error": "Customer not found"}, 404
        db.session.delete(customer)
        db.session.commit()
        return {"message": "Customer deleted"}, 200

api.add_resource(CustomerResource, '/customers', '/customers/<int:id>')
# Similar resources would be created for 'products' and 'orders'        

So, this wasn't quite there, but it's getting closer, it's missing the OrderResource and ProductResource implementations, but it's getting there.

One last prompt, hopefully?

Code is still missing, please add implementations for all resources and all the HTTP verbs.        

and the answer is:

I apologize for the confusion earlier. Here's the complete implementation of the HTTP verbs for each resource:        
# app.py
from flask import Flask, request
from flask_restful import Api, Resource
from models import db, Customer, Product, Order

app = Flask(__name__)
api = Api(app)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:////tmp/test.db'
db.init_app(app)

class CustomerResource(Resource):
    # ... existing code ...

api.add_resource(CustomerResource, '/customers', '/customers/<int:id>')

class ProductResource(Resource):
    def get(self, id=None):
        if id:
            product = Product.query.get(id)
            if not product:
                return {"error": "Product not found"}, 404
            return product.json()
        products = Product.query.all()
        return [product.json() for product in products], 200

    def post(self):
        name = request.json.get('name')
        new_product = Product(name=name)
        db.session.add(new_product)
        db.session.commit()
        return new_product.json(), 201

    def put(self, id):
        product = Product.query.get(id)
        if not product:
            return {"error": "Product not found"}, 404
        product.name = request.json.get('name')
        db.session.commit()
        return product.json(), 200

    def delete(self, id):
        product = Product.query.get(id)
        if not product:
            return {"error": "Product not found"}, 404
        db.session.delete(product)
        db.session.commit()
        return {"message": "Product deleted"}, 200

api.add_resource(ProductResource, '/products', '/products/<int:id>')

class OrderResource(Resource):
    def get(self, id=None):
        if id:
            order = Order.query.get(id)
            if not order:
                return {"error": "Order not found"}, 404
            return order.json()
        orders = Order.query.all()
        return [order.json() for order in orders], 200

    def post(self):
        customer_id = request.json.get('customer_id')
        product_id = request.json.get('product_id')
        new_order = Order(customer_id=customer_id, product_id=product_id)
        db.session.add(new_order)
        db.session.commit()
        return new_order.json(), 201

    def put(self, id):
        order = Order.query.get(id)
        if not order:
            return {"error": "Order not found"}, 404
        order.customer_id = request.json.get('customer_id')
        order.product_id = request.json.get('product_id')
        db.session.commit()
        return order.json(), 200

    def delete(self, id):
        order = Order.query.get(id)
        if not order:
            return {"error": "Order not found"}, 404
        db.session.delete(order)
        db.session.commit()
        return {"message": "Order deleted"}, 200

api.add_resource(OrderResource, '/orders', '/orders/<int:id>')        

Ok, let's analyze what we got:

  • we spent a few seconds crafting prompts.
  • some time to read the code to realize what's missing.

and the conclusion is that we got a complete Web API implementation, but we're probably wondering if a better crafted prompt would have reduced the back and forth.


And now, let's pivot

Let's do a final thing, namely let's simplify the code to just use static data, it might be too early to use a database if we for example don't know if the API is going to be used or not, so let's do that:

please remove SQLAlchemy and only use static data in the API, thanks        

and the resulting code:

# app.py
from flask import Flask, request
from flask_restful import Api, Resource

app = Flask(__name__)
api = Api(app)

# Static data
customers = [
    {"id": 1, "name": "John Doe"},
    {"id": 2, "name": "Jane Doe"}
]

products = [
    {"id": 1, "name": "Product 1"},
    {"id": 2, "name": "Product 2"}
]

orders = [
    {"id": 1, "customer_id": 1, "product_id": 2},
    {"id": 2, "customer_id": 2, "product_id": 1}
]

class CustomerResource(Resource):
    def get(self, id=None):
        if id:
            customer = next((c for c in customers if c["id"] == id), None)
            if not customer:
                return {"error": "Customer not found"}, 404
            return customer, 200
        return customers, 200

    # Implement POST, PUT, DELETE similarly

api.add_resource(CustomerResource, '/customers', '/customers/<int:id>')

class ProductResource(Resource):
    def get(self, id=None):
        if id:
            product = next((p for p in products if p["id"] == id), None)
            if not product:
                return {"error": "Product not found"}, 404
            return product, 200
        return products, 200

    # Implement POST, PUT, DELETE similarly

api.add_resource(ProductResource, '/products', '/products/<int:id>')

class OrderResource(Resource):
    def get(self, id=None):
        if id:
            order = next((o for o in orders if o["id"] == id), None)
            if not order:
                return {"error": "Order not found"}, 404
            return order, 200
        return orders, 200

    # Implement POST, PUT, DELETE similarly

api.add_resource(OrderResource, '/orders', '/orders/<int:id>')        

Ok, so now we lost the database, but we got static data, but it's still using the flask_restful library. I think you've seen enough on how to work with an AI assistant to build a Web API :)

A second example- brownfield, let's have it do boring stuff for us

In our first example, we used an AI assistant to build a Web API, but that might be too much, maybe your day-to-day is more about creating classes, adding methods, removing or adding fields, and so on, on existing code? The AI assistant can help even with those scenarios, let's see how.

Create a class

Creating a class, we've done that hundreds of times, we probably are using a snippet for it already. But let's have our AI assistant do it for us with the following prompt:

create a Product class with fields name, id, description, updated, created        

From what you can see above, we just give it the name of things, not what type they should be, let's hope for the best crossing fingers :)

Here's the response:

class Product:
    def __init__(self, name, id, description, updated, created):
        self.name = name
        self.id = id
        self.description = description
        self.updated = updated
        self.created = created        

Oh, I wasn't specific, I meant I wanted it in TypeScript, let's try again (yes it can produce code in many languages):

create a Product class with fields name, id, description, updated, created in Typescript        

with result:

class Product {
    id: number;
    name: string;
    description: string;
    updated: Date;
    created: Date;

    constructor(id: number, name: string, description: string, updated: Date, created: Date) {
        this.id = id;
        this.name = name;
        this.description = description;
        this.updated = updated;
        this.created = created;
    }
}        

Nice! Now you saw that the AI assistant can do many programming languages as well. What about adding a method to the class, let's try that:

add a method fullDescription that interpolates name, description        

with result:

class Product {
    id: number;
    name: string;
    description: string;
    updated: Date;
    created: Date;

    constructor(id: number, name: string, description: string, updated: Date, created: Date) {
        this.id = id;
        this.name = name;
        this.description = description;
        this.updated = updated;
        this.created = created;
    }

    fullDescription(): string {
        return `${this.name}: ${this. Description}`;
    }
}        

Let's try improving the code, let's see what we get:

improve the above code        

we get this response:

class Product {
    constructor(
        public id: number,
        public name: string,
        public description: string,
        public updated: Date,
        public created: Date
    ) {}

    fullDescription(): string {
        return `${this.name}: ${this. Description}`;
    }
}        

Nice, now we're talking.

Final experiment - RegEx

Regular expressions, RegEx, are a powerful tool, but they are also hard to get right, and let's be honest, we don't use them that often, so we forget how to use them. Can an AI assistant help us with that? Let's try it out.

Here's the RegEx we want to understand:

let emailRegex = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/;        

The name gives it away, it's about email, but can we get more information about it? Let's try:

explain emailRegex        

Ok, let's see what we get:

The emailRegex is a regular expression used to validate email addresses. Here's a breakdown of how it works:

- ^: This asserts the start of a line.
[a-zA-Z0-9._%+-]+: This matches one or more (+) of the enclosed characters. It includes lowercase letters (a-z), uppercase letters (A-Z), numbers (0-9), and the special characters . _ % + -.
- @: This matches the @ symbol.
[a-zA-Z0-9.-]+: This matches one or more (+) of the enclosed characters after the @ symbol. It includes lowercase letters (a-z), uppercase letters (A-Z), numbers (0-9), and the special characters . and -.
- \.: This matches the . symbol.
- [a-zA-Z]{2,}: This matches at least two ({2,}) of the enclosed characters. It includes lowercase letters (a-z) and uppercase letters (A-Z). This is typically used to match the domain part of the email (like com, org, net, etc.).
- $: This asserts the end of a line.        

Ok, great, we understand it a bit more, but let's do some scary stuff, namely update the RegEx to match a new condition, namely that the email address should only be allowed for the following domains: gmail, yahoo, hotmail, outlook, nothing else. Let's try it:

Update emailRegex to only allow gmail, yahoo, hotmail, outlook        

Here we go :)

let emailRegex = /^[a-zA-Z0-9._%+-]+@(gmail\.com|yahoo\.com|hotmail\.com|outlook\.com)$/;        

It looks like it could work, but as well know with code, we don't trust it unless we got tests that proves it, so let's create tests next:

generate tests for all possible permutations of the emailRegex        

and the response (in Jest code):

describe('emailRegex', () => {
    test('valid emails', () => {
        const emailRegex = /^[a-zA-Z0-9._%+-]+@(gmail\.com|yahoo\.com|hotmail\.com|outlook\.com)$/;
        expect('user@gmail.com'.match(emailRegex)).toBeTruthy();
        expect('user@yahoo.com'.match(emailRegex)).toBeTruthy();
        expect('user@hotmail.com'.match(emailRegex)).toBeTruthy();
        expect('user@outlook.com'.match(emailRegex)).toBeTruthy();
    });

    test('invalid emails', () => {
        const emailRegex = /^[a-zA-Z0-9._%+-]+@(gmail\.com|yahoo\.com|hotmail\.com|outlook\.com)$/;
        expect('user@other.com'.match(emailRegex)).toBeNull();
        expect('user@gmail'.match(emailRegex)).toBeNull();
        expect('user@.com'.match(emailRegex)).toBeNull();
        expect('user@com'.match(emailRegex)).toBeNull();
    });
});        

Ok, we got some tests written, but I'm pretty sure a lot of tests are missing here, let's see if the AI assistant can help us with that:

I said all possible permutations, please add tests to cover all possible scenarios        
describe('emailRegex', () => {
    const emailRegex = /^[a-zA-Z0-9._%+-]+@(gmail\.com|yahoo\.com|hotmail\.com|outlook\.com)$/;

    test('valid emails', () => {
        expect('user@gmail.com'.match(emailRegex)).toBeTruthy();
        expect('user@yahoo.com'.match(emailRegex)).toBeTruthy();
        expect('user@hotmail.com'.match(emailRegex)).toBeTruthy();
        expect('user@outlook.com'.match(emailRegex)).toBeTruthy();
        expect('user.name@gmail.com'.match(emailRegex)).toBeTruthy();
        expect('user_name@yahoo.com'.match(emailRegex)).toBeTruthy();
        expect('user-name@hotmail.com'.match(emailRegex)).toBeTruthy();
        expect('user+name@outlook.com'.match(emailRegex)).toBeTruthy();
    });

    test('invalid emails', () => {
        expect('user@other.com'.match(emailRegex)).toBeNull();
        expect('user@gmail'.match(emailRegex)).toBeNull();
        expect('user@.com'.match(emailRegex)).toBeNull();
        expect('user@com'.match(emailRegex)).toBeNull();
        expect('user@GMAIL.com'.match(emailRegex)).toBeNull();
        expect('user@Yahoo.Com'.match(emailRegex)).toBeNull();
        expect('user@hotmail.COM'.match(emailRegex)).toBeNull();
        expect('user@Outlook.Com'.match(emailRegex)).toBeNull();
        expect('user@outlookcom'.match(emailRegex)).toBeNull();
        expect('user@.gmail.com'.match(emailRegex)).toBeNull();
        expect('user@gmail..com'.match(emailRegex)).toBeNull();
        expect('@gmail.com'.match(emailRegex)).toBeNull();
        expect('user@'.match(emailRegex)).toBeNull();
        expect('@'.match(emailRegex)).toBeNull();
        expect(''.match(emailRegex)).toBeNull();
    });
});        

I think I want to have these tests divided up a bit for better oversight:

please divide it up into more tests        

Now we're talking :)

describe('emailRegex', () => {
    const emailRegex = /^[a-zA-Z0-9._%+-]+@(gmail\.com|yahoo\.com|hotmail\.com|outlook\.com)$/;

    test('valid emails', () => {
        expect('user@gmail.com'.match(emailRegex)).toBeTruthy();
        expect('user@yahoo.com'.match(emailRegex)).toBeTruthy();
        expect('user@hotmail.com'.match(emailRegex)).toBeTruthy();
        expect('user@outlook.com'.match(emailRegex)).toBeTruthy();
        expect('user.name@gmail.com'.match(emailRegex)).toBeTruthy();
        expect('user_name@yahoo.com'.match(emailRegex)).toBeTruthy();
        expect('user-name@hotmail.com'.match(emailRegex)).toBeTruthy();
        expect('user+name@outlook.com'.match(emailRegex)).toBeTruthy();
    });

    test('invalid emails - wrong domain', () => {
        expect('user@other.com'.match(emailRegex)).toBeNull();
        expect('user@gmail'.match(emailRegex)).toBeNull();
        expect('user@com'.match(emailRegex)).toBeNull();
    });

    test('invalid emails - wrong format', () => {
        expect('user@.com'.match(emailRegex)).toBeNull();
        expect('user@GMAIL.com'.match(emailRegex)).toBeNull();
        expect('user@Yahoo.Com'.match(emailRegex)).toBeNull();
        expect('user@hotmail.COM'.match(emailRegex)).toBeNull();
        expect('user@Outlook.Com'.match(emailRegex)).toBeNull();
        expect('user@outlookcom'.match(emailRegex)).toBeNull();
        expect('user@.gmail.com'.match(emailRegex)).toBeNull();
        expect('user@gmail..com'.match(emailRegex)).toBeNull();
    });

    test('invalid emails - missing parts', () => {
        expect('@gmail.com'.match(emailRegex)).toBeNull();
        expect('user@'.match(emailRegex)).toBeNull();
        expect('@'.match(emailRegex)).toBeNull();
        expect(''.match(emailRegex)).toBeNull();
    });
});        

Can we be sure that the RegEx is correct? Well, no, but we definitely have a bunch of tests which is good start. AND, we didn't write the RegEx ourselves, or update it which is even better :)

Jokes aside, we should understand the subject matter, at least on basic level, an AI assistant helps the best when you know your topic and you know what you want to do, but you don't want to do it manually.

References

Summary

This was a bit lengthy newsletter, but I hope you got some ideas on how to use AI assistants with source code. Don't worry I will definitely write more about this topic in the future.

Let's recap all the learnings:

  • AI assistants can help you write net new code for greenfield projects.
  • It can improve existing code.
  • You should never trust the code it generates, but you can use it as a starting point.
  • Ask it to generate tests, but you still need to verify that the tests are correct.
  • An AI assistant usually doesn't get it right the first time but keep iterating or ask it to change/improve the code.
  • You're the pilot, it's the copilot, you're in control.
  • It's a new tool, just like snippets,
  • It's not magic (but getting there :) ), but definitely helps.

Good luck out there :)

Please comment on this newsletter, I would love to hear your thoughts. Happy to take ideas for future newsletters as well.

What's next?

This was a bit code heavy, but for the next issue, let's talk about presentations, how can an AI assistant help you with that? Everyone from CEO to developer needs to do presentations sooner or later, but let's use an AI assistant to help us with that.

Best Chris















Haimantika Mitra

Developer Advocate| Author | Microsoft AI-900 certified | Working on developer education, developer experience, documentation and GenAI.

11mo

Loved it! Thank you for sharing it!

To view or add a comment, sign in

Insights from the community

Others also viewed

Explore topics