Video summary
The video explores how to transition away from Object-Relational Mappers (ORMs) by leveraging Python's static type checker, mypy, to create more robust and maintainable applications. The speaker argues that while ORMs offer quick development speeds through patterns like those found in Django or Rails, they can sometimes obscure data integrity issues until runtime errors occur. By using mypy, developers can catch logical errors and type mismatches before the code is even executed, providing immediate feedback on what might be wrong without needing to run the application. This approach shifts error detection from the database layer to the development environment, allowing for earlier identification of bugs related to data validation and API usage.
A core theme of the talk is the power of composition over inheritance for building flexible architectures that hide internal complexities. The speaker demonstrates how defining custom classes with specific attributes using TypedDict allows developers to enforce strict contracts on data structures like books, authors, and publishers. For instance, if a model is defined to require a publisher, mypy will immediately flag any attempt to create an instance without one or try to save such an incomplete object to the database. This static analysis guides refactoring by highlighting missing attributes or incorrect type assumptions, ensuring that the codebase remains consistent and that critical data fields are never accidentally omitted during development.
The presentation also addresses the nuances of handling optional data and complex relationships, such as books belonging to a series. The speaker illustrates how mypy handles scenarios where a database query might return no results, requiring the use of Optional types to represent values that could be either an object or None. When implementing logic like finding related books in a series, the type checker helps manage the transition between generic book objects and specific series-bound objects. Although mypy may occasionally need assistance via explicit casting when dealing with dynamic attributes, it effectively enforces that subclasses inherit parent properties, preventing errors where code assumes an attribute exists without verifying its presence.
Ultimately, the video concludes that mypy is not a replacement for unit tests but a complementary tool that significantly boosts confidence in code correctness. It acts as a continuous guide during development, surfacing cases that developers might have overlooked and ensuring that all edge cases are handled properly before deployment. By integrating type annotations early in the process, teams can maintain a high standard of data integrity and reduce the likelihood of runtime failures caused by mismatched data types or missing fields. The speaker emphasizes that while mypy cannot solve every problem alone, it is an invaluable asset for discovering forgotten logic, enforcing architectural constraints, and streamlining the path to building reliable software systems.
Read the full video transcript
as the user sent in bad data they can
you can the application can error before
it even gets to the database possibly
tell the use of something a little bit
more useful in terms of what they did
wrong if they're using an API and then
there's a lot of simple patterns that
just come with the rme that make it easy
to build out on your application quickly
that's why things like Django and rails
and stuff are so popular because you can
type a few things in and you get like an
application really fast and that's
definitely cool but there are other ways
to get at this
so composition is I wrote functional
composition here but I mean even object
composition is is good of generally
being able to compose things makes
things more flexible and also lets you
hide things so if instead of a subclass
you contain a class you can hide that
class from the external world and you
can trust that I've got my model class
but only my model class can talk to the
database nobody else can talk to the
database and I don't have to worry about
queries leaking in somewhere I wasn't
expecting or things like that so
composition is a great tool for
abstracting away complexity that you
don't need to know about in the function
you're writing or in the module you're
writing there's a lot of built-in data
structures in Python that provide a lot
of these have a lot of power to them you
can also like I said you can just write
an out you can just write a class you
can build your own class hierarchy etc
there's one more thing which is going to
be the main talk of my main focus of my
talk that you can use and that is my pie
so talks about my pie it talks about our
m's what do they have to do with each
other they actually have nothing to do
with each other my pie has nothing to do
with databases my pie is a static type
checker have been has anybody used my
pie mostly on that side of the room ok
a few people like that so it's a static
type checker you can use it to tell it
will tell you if you're doing something
wrong in your Python code without
actually running your Python code and it
uses type annotations if you're using a
version of Python that supports type
annotations in the syntax alternatively
you can run it against old code you can
run against Python 2 code and you do
your type annotations in comments and it
can parse the comments out and figure
out the types so let's show off a little
bit of what it's going to look like so
I'm gonna open up file here sure no
hitting the wrong character
okay so I'm going to write a function
and I'm going to annotate its type so
this is just from the mypie example but
just so we see what it is
I've got greeting it's gonna take some
name we're gonna give it a type which is
a string the colon means this is its
type and then the function has a return
type and that's what the arrow means
this is what it's going to return it's
also going to return a string and there
we go okay so right now there's no
errors because the function isn't
actually doing anything even though if
you call this function or return none my
PI is saying that's okay you haven't
actually written anything yet if I
returned an integer it will tell me
incompatible return type I expected a
string but I got an integer you're doing
something wrong I can say okay well how
about just saying hello and it says okay
cool
one thing to note here is that I'm
taking the name I'm not doing anything
on the name that's probably not what I
intended my PI is good at checking to
make sure that you're handling
conditions that you're not accessing
things that aren't there stuff like that
what it can't do is tell you if you're
doing the right thing because it doesn't
know what the right thing is so there's
a you know there's a lot of discussion
about types and tests and which is
better you really kind of need both if
you want to be really confident that
your tests your code is working
correctly so it's not a replacement for
writing unit tests but it I do find that
it's helpful for guiding you saying like
hey you forgot about this case maybe you
need to handle this case probably want
to test to say that the right thing
happens in that case but it will at
least tell you that you handle the case
you
so here similarly if I say hello +5 it's
gonna say I can't do that because you
can't add strings of numbers but if I
say hello + name it's totally fine so
down here I just have my editor telling
me what's wrong with the current file
I'm working on so let's start using my
PI instead of an ORM so the very first
thing we want to do is define sort of
our model I'm gonna use I'm gonna use
something called a typed dictionary
comes from my PI type extensions I think
at some point in the future this is
going to be merged into the standard
typing module but for now it's an
extension while they're sort of
solidifying the API and I want to I'm
going to define let's say we're building
an application for dealing with books
cuz books are you know really cool and
modern and etc so I'm gonna use a typed
dictionary type dictionary is just it
says look in at runtime represent this
object as just a dictionary but I want
to say what attributes it has to have
and what types those attributes are and
so folks have authors sure and they've
got titles okay so there we go
I should probably type out the code
correctly
every time okay so that's useful maybe
haven't done anything with it yet let's
write a function that can take a
dictionary maybe it's coming from the
user JSON request somewhere else and
return one of these things so we get a
dictionary coming in call it request
it's a dictionary but we want to get a
book back out so we can return a book by
the brook and it has an author
okay so it says that's
all right so we've eliminated all of our
typos and it says everything's cool now
one thing you'll notice here is that
this function won't always run to
completion if I pass a dictionary in
that doesn't have these arguments it
will raise an exception and won't return
anything it won't return a book won't
return anything
maybe that's fine maybe we want to do
something a different way we can talk
about other ways to do it at the end of
the talk if I get that far but just sort
of be aware that static type checking
checking works great on functions that
return values it doesn't work great on
things that raise exceptions it can't
tell you that you mutated the state
necessarily it's really about pure
functions or what it handles best so
we've got this function we can give it a
dictionary a generic dictionary we have
no idea what's in it and it will either
return a book or it will raise an
exception okay so let's say we want to
be able to save this book into our
database so I have written a fake
database here but I'm gonna import and
it just has it has an insert function so
I'm going to I want a function that
saves a book it takes a book to save the
book has to be a book not a book and it
doesn't return anything and then I'm
going to insert the book into my
database I'm going to pretend like it's
sort of II except I called it
insert instead of instead of
at insert or whatever whatever it's got
a I want to save the title and I want to
save the author in the database okay
everything's good cool now authors are
good and all but the publisher is where
the money is so we really need to keep
track of the publisher because they are
the people with the money so we are
going to first let's pretend like you
know what we need to remember to save
the publisher okay oh oh that's
interesting it's actually telling me
that I can't do that because books don't
have publishers so I can come up here
and I can say okay books have publishers
now oh but now it's saying I can't
create a book without a publisher
because I said books have to have
publishers so now
so you can see what it's doing here is
it's kind of guiding my refactoring it's
letting it's sort of telling me you
forgot about this you forgot about that
it's keeping track of what you can and
can't do with the values that are
passing through your your code and it's
telling you if you're doing something
that will potentially fail at runtime so
that's kind of cool so let's add
something else
so some books come in series not all
books come in series but some books are
a book in a series so let's try to model
that so we're gonna start out by just
defining a new class book in series and
it's kind of book you can inherit from
it it's not really inheritance because
remember a runtime these are just
dictionaries but it's sort of a hint to
the type checker um by inheriting from
book it gets all of the attributes of
book and then it can have new attributes
so I can say I also have a series it's
just the name of the series the ID
whatever I'm just going to store it as a
string okay
that is cool but it's not really doing
anything useful right now but we can use
it to add a new feature let's say our
website we want to be able to find
things books related to books
recommendations engines were a really
big deal 15 years ago
still are probably people don't brag
about them as much because they all just
bought them so we're gonna write a
function called fine related and it's
gonna find one related book or it's
going to tell me that there aren't any
related books so it's gonna take a book
its type is book and we want to return
we want it to return the book makes
sense right but I don't really know how
to implement it yet so I'm just gonna
pretend
I don't know I'm gonna figure something
out okay so how could we go about
finding a related book well if the book
is in a series then we can just find
another book in the same series right
they're presumably related so we're
gonna write a function find in series
that's gonna take a book but that book
has to have a series so book in series
and it's going to return a book in
series okay so we want to somehow do
this I've got this database class it has
the Select function or not class
module has a find I actually called it
find and see I want to say again sort of
pretending like we're riding against
is interesting in this case
because it doesn't enforce anything so
you really need to enforce things at the
application level if you want anything
enforced at all so I'm gonna find a book
here okay I've got a return the thing I
find okay this is interesting so the
find implementation I wrote says I'm not
always going to find the thing you're
asking for maybe that thing doesn't
exist in the database
so it uses something called optional so
I'm gonna go import optional here
because I'm gonna need it
optional okay and then this is returning
an optional book in series still not
right because I'm trying I'm trying to
return an optional let me if you look at
the very bottom here I'll tell you the
whole whole problem it's it got a
optional dictionary of anything to
anything but I told that I was going to
return an optional book in series so
that's because I need something similar
to a parse book but for a book that has
a series attached to it so just
copy-paste that's a good programming
practice so I'm gonna do this book in
series and I want it to return a book in
series okay I'm probably done right no
I'm not okay so we need to return it
says I need to I said I would give it a
book in series but I gave it a book so
book with a series okay and then I'm
gonna also break the summer starting to
long so I should be good right no okay
so book and series needs a series I told
it I needed a series it's telling me
that saying key series missing from type
dik-dik to booked in series so let's do
that
ok so now our spoken series is written
correctly but this still isn't written
correctly so what I'm gonna do is I'm
going to say well I'm gonna parse book
in series and that will turn my JSON
object thing dictionary into a book in a
series seems like it should be a good
idea still won't let me do it because I
typed it because I typed a book twice
book ok but it really wasn't supposed to
let me do it anyway because Parsa book
and series expects to get a dictionary
but the database may not return a
dictionary it may just return none
optional means you're either gonna
return this or you're gonna return none
it's a sort of a subclass of another
type called Union which is one or the
other but the other is always none so
here again it's telling me hey you
forgot that sometimes the thing you're
looking for isn't in the database and
you don't get anything back and if you
run this you'll get like a null like
none type has no blah blah blah
very annoying error so let's save this
off save off what we get and then we can
say if Rho is not no
returned parce book and series row
otherwise we'll return none okay cool so
we can actually find go to the database
and say hey find a book in this same
series if you don't find it return
nothing if you do find it turn it into
this book and series thing and return it
back to me okay
that sounds cool but we still haven't
implemented this find related things so
let's figure out how to implement that
so what we want to do is be able to say
hey look when you asked me to find a
related book if the book belongs to a
series I know how to do that I can just
find another book in the same series and
tell you to buy that book if if it's not
in a series I haven't figured it out yet
we've got to do some machine learning
thing or something and I whatever we'll
just say nothing so I am going to here
say let's try this let's just say find
in series book okay there's a couple
things wrong with that so trying to
return the wrong thing but also find in
series wants a book that has a series
attached to it we're not sure if the
series is attached to it because we
haven't checked so let's try this
if series in book
let's do that otherwise our return none
is gonna find anything okay so the first
thing is sometimes we return none and we
said we'll always return a block but we
actually know that we're not always
gonna return a book sometimes there is
no nothing related to the book it's just
like a magical unicorn book
it's Jean refining and nobody has caught
on to the hot new genre okay so fine
related has to be optional we don't know
we can't always find something related
to it there's still a problem the
problem is that find in series wants a
book in series a book with a series
attached to it but we only have a book
so this is actually one of the places
that my PI kind of has trouble we know
by looking at this code that add this
line here the book has a series
attribute but my PI doesn't it's gotten
confused fortunately there's a way
around that which is cast so if you've
ever in any C++ code or Java code you
know what that does and we have to
import it okay so we've told my PI look
trust me I checked I'm very certain that
book in series is the right type one
thing we didn't have to tell it though
is that a book in series is a book
because we inherited the types from each
other even though here I'm saying I want
to return a book and here I'm saying I'm
gonna return a book with a series
attached to it my PI knows that a book
with a series attached to it is also a
book so it lets us do it so that is that
and then one last thing that we need to
do we need to be able to save the book
so
so I want to save the book I want to
save the book in series and I should
probably save the series huh right
no as I forgot to update the type so
there we go so the type checker is kind
of assisting me as I'm refactoring my
code and it's saying hey you forgot
about this you you need to check for
this attribute this attribute doesn't
exist so the other thing it does is it
says you can trust that this attribute
exists if this was some whole very
complicated sub that dealt with books
they were belong to a series and it
needed to find an image that's the
series image instead of the book's cover
image or give you a listing of all the
books in order you you can trust no
matter how many other functions you call
that the book has that attribute and if
you make a mistake as I did many times
up here the checker will check it for
you so that is it and I don't know if
there's time for any questions or not
[Applause]
feel to your just fix the problems until
yeah so if you go add a field manually
any code that doesn't access the field
MiFi doesn't know you're supposed to be
accessing accessing the field this is a
little hand wavy here I think that what
you would probably want to do is build
out that interface layer between your
code in your database you want a unit
test that right and so you're kind of
doing this in conjunction with writing
tests so at some point you'd have a test
that says hey look if the user gives me
a book that has a series attached to it
I better save the series attached to it
in the database you're gonna have to go
update the database saving code to write
the series out at that point that sort
of drives you to go start adding the
type annotation and then my PI helps you
find all the places that you might have
broken and go in and sort of fix them up
so it's not it can do everything for you
but it can help guide you and show you
places where maybe you forgot that you
did something over here or whatever so
that's what it's really good at is
discovering those things okay I think
that's time
[Applause]
[Music]