Tach is a Python tool to enforce dependencies and interfaces, written in Rust.
Tach is inspired by the modular monolith architecture.
Tach.Demo.mp4
Tach can enforce:
- π Imports only come from declared dependencies
- π€ Cross-module calls use the public interface
- βοΈβπ₯ No cycles in the dependency graph
Tach is:
- π Open source
- π Installable via pip
- π§ Able to be adopted incrementally
- β‘ Implemented with no runtime impact
- βΎοΈ Interoperable with your existing systems
Tach will guide you through initial project setup.
Run:
After an introductory message, you will see a file tree interface allowing you to interactively configure your project.
Use the arrow keys to navigate, and mark each module boundary with ‘Enter’. You can mark all of your top-level Python packages, or just a few that you want to track.
If your Python code lives below your project root, or if you are working in a monorepo with multiple Python packages, mark your Python source roots using the ‘s’ key.
Tach comes with a cli command to enforce the boundaries that you just set up! From the root of your Python project, run:
You will see:
You can validate that Tach is working by:
- Removing an item from the
depends_on
key intach.toml
, or marking it as deprecated - By adding an import between modules that didn’t previously import from each other.
Give both a try and run tach check
again. This will generate an error:
β tach/check.py[L8]: Cannot use 'tach.filesystem'. Module 'tach' cannot depend on 'tach.filesystem'.
Each error indicates an import which violates your dependencies. If your terminal supports hyperlinks, click on the file path to go directly to the error.
When an error is detected, tach check
will exit with a non-zero code. It can be easily integrated with CI/CD, Pre-commit hooks, and VS Code, and more!
Visualize your dependency graph.
Tach will generate a graph of your dependencies. Here’s what this looks like for Tach:
Note that this graph is generated remotely with the contents of your tach.toml
when running tach show --web
.
If you would like to use the GraphViz DOT format locally, simply running tach show
will generate tach_module_graph.dot
in your working directory.
You can view the dependencies and usages for a given path:
tach report my_package/
# OR
tach report my_module.py
e.g.:
9 Comments
jtwaleson
Cool! Do you have plans to launch a paid offering? The website makes me think it's a company but I didn't see any pricing / sales details.
vednig
This is pretty cool, thanks for sharing
hansonkd
Really excited to see this project gain traction.
> Note that this graph is generated remotely with the contents of your `tach.toml`
Isn't shipping off parts of your codebase to a 3rd party without warning in the CLI a security risk? Or in regulatory environments you get audited that your code was only stored on properly vetted services which is why some sales cycles for AI coding assistant tools are so long. It would be kind of frustrating to have something like that happen and get set back on licensing, etc.
Just from the video it doesn't seem like any sort of warning that you are shipping config files to your servers and the URL that you produced doesn't seem to have any authentication.
Maybe i am misunderstanding that functionality, but it gives me pause to use it.
lijok
Tools like this rub me the wrong way.
We have well established conventions like prefixing private modules and symbols with an underscore, or declaring your public interfaces in the __init__.py file, but the Python developer decries it as "busywork", "weird" and "hard to read", so we instead use tools like this.
We can manage dependencies with protocols, a type checker and generally following SOLID principles, but the Python developer decries it as "too indirect and convoluted", so we instead use tools like this.
This is more commentary on the Python developer than this tool. Tach looks great.
adamc
Having the example be a video that changes was confusing at first, and if you are going to show me something that is changing, I would like to be able to rewind to the beginning. But really I just think it's a bad idea to show something like that without making it obvious what it is.
benrutter
This looks nice! I vaguely know Grimp as a similar tool, any idea how they differ/compare?
tracnar
When I tried it, it seemed like you really need to list all modules in `tach.toml`.
What I wanted was to work at a coarser package level. For example if you have the modules `foo.a`, `foo.b`, `bar.a`, and `bar.b`, I'd like a rule that `bar` can import from `foo` but not vice versa, without having to list or care about the submodules.
Is that something you'd want to support?
stavros
We replaced our microservices architecture with a modular monolith and got tons of benefits, something I've been meaning to write up. However, while discussing that here, I was pointed to Tach, which looks fantastic if you have (or want to create) a modular monolith.
rubenvanwyk
Does this work together with uv?