1

I'm looking at creating my first packages to clean up my codebase.

Below is a very simplified version of my current project structure:

my_project/
|-database.py
|-app_1.py
|-app_2.py
|-constants.py

To explain each one a little bit:

  • database.py - all the sqlalchemy orm table classes used by app_1.py and app_2.py
  • app_1.py - an application that interacts with the database
  • app_2.py - an application that interacts with the database
  • constants.py - a range of constants used by different combos of the other files

Worth mentioning that in real life each one of the first three files is actually multiple folders and dozens of files.

To clean things up I'm considering creating packages for the first three files however I'm not sure how to deal with the constants. In some cases they are used in all of the first three files.

I've come up with three possible options:

  1. Duplicate the constants in each of the packages
  2. Create a dedicated constants package and import within each package
  3. Create a constants text file with all the constants and load it from each package

Option 1 doesn't seem like good coding practice and I can't find any references to anyone going with Option 2. This seems to leave me with Option 3. However, I then can't think where I would store the text file as it doesn't have a natural repository home!

Is there a best practice way to deal with the situation I'm facing?

2
  • 3
    Possibly you can't find any explicit references to option 2) because everyone considers it the obvious choice according to well-known design principles. I know I would. Commented Jan 25, 2023 at 22:48
  • @KilianFoth: the choice may not be not so obvious as you think. Putting a bunch of constants into one package which are unrelated to each other has a potential to create undesired dependencies. This can get a real issue in languages like C++ and large projects, where a global "catch all constants" module can cause really ugly compile times - introducing one new constant which is needed by 3 modules will cause 50 other modules to recompile which don't need that constant.
    – Doc Brown
    Commented Jan 26, 2023 at 7:16

2 Answers 2

6

Putting all constants into one package is a strategy which mostly works for smaller projects, and may be completely sufficient for your case.

However, that approach does not scale well when a project grows over time, because putting several constants which are unrelated to each other into one package creates artificial dependencies between other packages, which makes individual versioning, individual development on isolated packages and individual reuse hard.

This can be avoided by tailoring packages and the contained constants not by syntactical criteria (like "is something a constant"), but by semantical means or functional topics.

For example, lets say you have a constant for your database connection string. One option is to place this directly in the database.py package. However, if you want to reuse the database.py package in other projects with different connection strings, that may not be a good idea, so you introduce a package databaseConfiguration.py, where the constant lives. database.py will not depend on that, but the applications which use that constant and pass its value to some database connector.

Later, you may find out that the database connection string is used in several similar looking functions in different applications (but still not general ennough for database.py). Then you can refactor the common code out and put it also into databaseConfiguration.py.

Your next group of constants may be a list of special numbers from trigonometry, like Pi/3 or sqrt(2). You may create a package JossyMath.py, and put not just the constant there, but also math functions which do something useful with the constant.

Next constant may be a copyright string you want to present in several applications. This could be part of a package copyrightInfo.py, or generalAppInformation.py, if you have several of those.

In the end, sometimes there may remain some few constants which are not easily assignable to a functional topic. When you cannot get rid of them, by no means, leave them in aconstants.py package. However, avoid to make other reuseable modules depend on them, because that makes it hard to reuse them in a different context.

In short, you have to decide this case-by-case, constant-by-constant, where the best place is, and when you do this by functional topics, you will rarely end up with "constants-only" packages.

2

I think a good question to ask yourself is how often will I need to update the constants or list of constants? Will I need to add more? If this is expected to change frequently and needed to keep the 3 packages in sync, option 2 is probably better since you can roll out these changes in one place and have them propagate out to the other packages.

If the list of constants is short, and they will rarely change, duplicating them is probably fine (option 1), since you only have to do it once.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.