Live data from Hacker News

Architecture Patterns with Python

cosmicpython.com

71–80 of 143 posts

Re: Architecture Patterns with Python

#71

Earlier quoted context omitted.

Thanks. In my mind, anything about complex structures of (abstract) classes and/or inheritance trees has nothing to do with a Repository pattern. As I understand it, Repository pattern is basically a generalization of the Data Access Object (DAO) pattern, and sometimes treated synonymously. The way I mean it and implement it, is basically for each entity have a separate class to provide the database access. E.g. you…

> for each entity have a separate class to provide the database access Let me correct you: for each entity that needs database access . This is why I'm talking about layers here: sometimes entities are never persisted directly, but only as "parts" or "relations" of other entities; in other cases you might have a very complex persistence implementation (e.g. some entities are stored in a RDB, while others in a filesys…

Naturally, Repository is a pattern for data(base) access, so it should have nothing to do with objects that are not persisted. I used "entity" as meaning a persisted object. That was not very clear, sorry.

Re: Architecture Patterns with Python

#72
post #41

Wow this book is a goldmine for architecture patterns. I love how easy it is to get into a topic and quickly grasp it. Having said that, from a practical and experience standpoint, using some of these patterns can really spiral out into an increased complexity and performance issues in Python, specially when you use already opinionated frameworks like Django which already uses the ActiveRecord pattern. I’ve been in c…

Strict architectural pattern usage requires understanding the domain, and understanding the patterns. If you have both, navigating the codebase will be intuitive. If you don't, you'll find 1000 LOC functions easier to parse.

Yes, if you have a PhD in the organization's domain model, anything is possible. Any mess of code, whether "clean" or not, can be reasoned through.

The problem is this takes years of on-site experience to attain this level of domain understanding.

Re: Architecture Patterns with Python

#73

Wow this book is a goldmine for architecture patterns. I love how easy it is to get into a topic and quickly grasp it. Having said that, from a practical and experience standpoint, using some of these patterns can really spiral out into an increased complexity and performance issues in Python, specially when you use already opinionated frameworks like Django which already uses the ActiveRecord pattern. I’ve been in c…

>I’ve been in companies big and small using Python, both using and ignoring architectural patterns. Turns out all the big ones with strict architectural (n=3) pattern usage, although “clean”, the code is waaaay to complex and unnecessarily slow in tasks that at first glance should had been simple.

The problem with "strict architectural pattern usage" is that people think that a specific implementation, as listed in the reference, is "the pattern".

"The pattern" is the thought process behind what you're doing, and the plan for working with it, and the highest-level design of the API you want to offer to the rest of the code.

A state machine in Python, thanks to functions being objects, can often just be a group of functions that return each other, and an iteration of "f = f(x)". Sometimes people suggest using a Borg pattern in Python rather than a Singleton, but often what you really want is to just use the module. `sys` is making it a singleton for you already. "Dependency injection" is often just a fancy term for passing an argument (possibly another function) to a function. A Flyweight isn't a thing; it's just the technique of interning. The Command pattern described in TFA was half the point of Jack Diederich's famous rant (https://www.youtube.com/watch?v=o9pEzgHorH0); `functools.partial` is your friend.

> Maybe this says something about me more than the code but I hate to admit I was more productive in the non clean code companies.

I think you've come to draw a false dichotomy because you just haven't seen anything better. Short functions don't require complex class hierarchies to exist. They don't require classes to exist at all.

Object-oriented programming is about objects, not classes. If it were about classes, it would be called class-oriented programming.

Re: Architecture Patterns with Python

#74
post #57

Earlier quoted context omitted.

Type annotations are just like documentation though. Just because the annotation says int the function can still return a list.

Annotations can and should be checked. If I change a parameter type, other code using the function will now show errors. That won't happen with just documentation.

In some cases don't you need to actually execute the code to know what the type actually is. How does the type checker know then?

Re: Architecture Patterns with Python

#75
post #4

Truly one of the great python programming books. The one thing that I found missing was the lack of static typing in the code, but that was a deliberate decision by the authors.

Some examples use dataclasses, which force type annotations.

Python does not support static typing. Tooling based on type annotations doesn't affect the compilation process (unless you use metaprogramming, like dataclasses do) and cannot force Python to reject the code; it only offers diagnostics.

Re: Architecture Patterns with Python

#76
post #57

Earlier quoted context omitted.

Type annotations are just like documentation though. Just because the annotation says int the function can still return a list.

Annotations can and should be checked. If I change a parameter type, other code using the function will now show errors. That won't happen with just documentation.

> Annotations can and should be checked

Unfortunately Python’s type system is unsound. It’s possible to pass all the checks and yet still have a function annotated `int` that returns a `list`.

Re: Architecture Patterns with Python

#77
post #22

Earlier quoted context omitted.

My experience is that once people have static typing to lean on they focus much less on the things that in my view are more crucial to building clean, readable code: good, consistent naming and small chunks. Just the visual clutter of adding type annotations can make the code flow less immediately clear and then due to broken windows syndrome people naturally care less and less about visual clarity.

So far off from what actually happens. The type annotations provide an easy scaffolding for understand what the code does in detail when reading making code flow and logic less ambiguous. Reading Python functions in isolation, you might not even know what data/structure you’re getting as input… if there’s something that muddles up immediate clarity it’s ambiguity about what data code is operating on.

>So far off from what actually happens

I disagree strongly, based on 20 years of using Python without annotations and ~5 years of seeing people ask questions about how to do advanced things with types. And based on reading Python code, and comparing that to how I feel when reading code in any manifest-typed language.

>Reading Python functions in isolation, you might not even know what data/structure you’re getting as input

I'm concerned with what capabilities the input offers, not the name given to one particular implementation of that set of capabilities. If I have to think about it in any more detail than "`ducks` is an iterable of Ducklike" (n.b.: a code definition for an ABC need not actually exist; it would be dead code that just complicates method resolution) I'm trying to do too much in that function. If I have to care about whether the iterable is a list or a string (given that length-1 strings satisfy the ABC), I'm either trying to do the wrong thing or using the wrong language.

> if there’s something that muddles up immediate clarity it’s ambiguity about what data code is operating on.

There is no ambiguity. There is just disregard for things that don't actually matter, and designing to make sure that they indeed don't matter.

Re: Architecture Patterns with Python

#78

Earlier quoted context omitted.

So far off from what actually happens. The type annotations provide an easy scaffolding for understand what the code does in detail when reading making code flow and logic less ambiguous. Reading Python functions in isolation, you might not even know what data/structure you’re getting as input… if there’s something that muddles up immediate clarity it’s ambiguity about what data code is operating on.

Exactly my experience. I call Python a surprise-typed language. You might write a function assuming its input is a list, but then somebody passes it a string, you can iterate over it so the function returns something, but not what you would have expected, and things get deeply weird somewhere else in your codebase as a result. Surprise! Type checking on the other hand makes duck typing awesome. All the flexibility, n…

This is because of Python's special handling of iteration and subscripting for strings (so as to avoid having a separate character type), not because of the duck typing. In ordinary circumstances (e.g. unless you need to be careful about a base case for recursion - but that would cause a local fault and not "deep weirdness at a distance"), the result is completely intuitive (e.g. you ask it to add each element of a sequence to some other container, and it does exactly that), and I've written code that used these properties very intentionally.

If you passed a string expecting it to be treated as an atomic value rather than as a sequence (i.e. you made a mistake and want a type checker to catch it for you), there are many other things you can do to avoid creating that expectation in the first place.

Re: Architecture Patterns with Python

#79

Wow this book is a goldmine for architecture patterns. I love how easy it is to get into a topic and quickly grasp it. Having said that, from a practical and experience standpoint, using some of these patterns can really spiral out into an increased complexity and performance issues in Python, specially when you use already opinionated frameworks like Django which already uses the ActiveRecord pattern. I’ve been in c…

> Turns out all the big ones with strict architectural (n=3) pattern usage, although “clean”, the code is waaaay to complex and unnecessarily slow in tasks that at first glance should had been simple.

My last job had a Python codebase just like this. Lots of patterns, implemented by people who wanted to do things "right," and it was a big slow mess. You can't get away with nearly as much in Python (pre-JIT, anyway) as you can in a natively compiled language or a JVM language. Every layer of indirection gets executed in the interpreter every single time.

What bothers me about this book and other books that are prescriptive about application architecture is that it pushes people towards baking in all the complexity right at the start, regardless of requirements, instead of adding complexity in response to real demands. You end up implementing both the complexity you need now and the complexity you don't need. You implement the complexity you'll need in two years if the product grows, and you place that complexity on the backs of the small team you have now, at the cost of functionality you need to make the product successful.

To me, that's architectural malpractice. Even worse, it affects how the programmers on your team think. They start thinking that it's always a good idea to make code more abstract. Your code gets bloated with ghosts of dreamed-of future functionality, layers that could hypothetically support future needs if those needs emerged. A culture of "more is better" can really take off with junior programmers who are eager to do good work, and they start implementing general frameworks on top of everything they do, making the codebase progressively more complex and harder to work in. And when a need they anticipated emerges in reality, the code they wrote to prepare for it usually turns out to be a liability.

Looking back on the large codebases I've worked with, they all have had areas where demands were simple and very little complexity was needed. The ones where the developers accepted their good luck and left those parts of the codebase simple were the ones that were relatively trouble-free and could evolve to meet new demands. The ones where the developers did things "right" and made every part of the codebase equally complex were overengineered messes that struggled under their own weight.

My preferred definition of architecture is the subset of design decisions that will be costly to change in the future. It follows that a goal of good design is minimizing architecture, avoiding choices that are costly to walk back. In software, the decision to ignore a problem you don't have is very rarely an expensive decision to undo. When a problem arises, it is almost always cheaper and easier to start from scratch than to adapt a solution that was created when the problem existed only in your head. The rare exceptions to this are extremely important, and from the point of view of optics, it always looks smarter and more responsible to have solved a problem incorrectly than not to have solved it at all, but we shouldn't make the mistake of identifying our worth and responsibility solely with those exceptions.

Re: Architecture Patterns with Python

#80
post #68

Earlier quoted context omitted.

SQLAlchemy Session is actually a unit of work (UoW), which they also build on top. By the end of the book they are using their UoW to collect and dispatch events emitted by the services. How would they have done that if they just used SQLAlchemy directly? You might argue that they should have waited until they wanted their own UoW behaviour before actually implementing it, but that means by the time they need it they…

I understand not everyone agrees on what "repository" means. The session is a UoW (at two or three levels) and also a repository (in the sense of object-scoped persistence) and also like four other things. I'm sort of tolerant of bits of Session leaking into things. I'd argue that its leaking pieces are the application-level things you'd implement, not versions of them from the lower layers that you need to wrap. Whe…

You seem to have stopped reading my comment after the first sentence. I asked some specific questions about how you would do what they did if you just use SQLAlchemy as your repository/UoW.
Post reply on HN