The creeping scourge of tooling config files in project root directories
github.com
github.com
If you want to put (most of) your config files in a .config folder, then most apps should support that
If you want to view your config files in a different way, then actually you're trying to solve a different problem. Perhaps hide show toggling or automatic grouping (see macOS desktop stacks) with your fs viewer would be a better area to tackle.
Finally, just wanted to say, everything at root level is usually config or a folder, so if I want to understand what tooling a project uses, I know immediately where to go, code is always in a "src" or similar. It sounds like this is a feature, not a bug.
Suppose this pushes through and .config becomes more of a default than a conscious project choice. I don't see all tooling packages to follow-through immediately with it - causing a period where you still have the scourge of some config files in project root, while others are at .config.
This makes sense for configs that could ostensibly do this, but that's only a fraction of them. Regardless of what we do about those (if anything), the other ones at least shouldn't be laid out like this.
Who/what is the "we" here? Last I checked my homefolder was still chock full of dotfiles in-use.
Similarly, things also like to hide state in ~/.cache
If they didn’t then rm -rf .appname would restore it to default settings, and we can’t have that.
This is the sort of issue that leads to “this page intentionally blank” type use of resources.
“When everything is a hammer...” when all you have to work with is a file system it always looks messy.
I’m more interested in making sure the right values exist in memory for runtime use.
Source, config, and comments could all live in one file and be specially delimited for look up too.
It’s all arbitrary at that point. This is really focusing on the wrong issues in computing.
The list of configuration files/directories is known. Why can't those who bother present them as they desire? Instead of forcing their will on others.
There reason for ~/.config is separation from data and cache. And the reason behind XDG Base Directory Specification is a generic way to run application with another config `XDG_DATA_HOME=foo bar`.
One dotfile has the maintainer's eccentric syntax highlighting preferences, another has critical build system settings needed to make any use of the code. Which ones do I really need to knand which are just conveniences for users of a particular IDE?
There are very few pieces of information where, to me, the natural place to look for it is "a dotfile at the root of the project." gitignore is an exception here.
Gets me every time when a maintainer puts Dockerfile in some random subdirectory.
If the root dir is full of dozens of files from all the tooling needed to build your software, maybe you have another problem than directory structure...
You have a standard JSON config file “.config.json” that resides in the root folder with a global object, with each key containing the config for each tool.
Collisions might be a concern, but you could use the tool’s url as the key so it’s unique. You are already seeing runtimes like Deno and Go do that for importing source code. Couldn’t the same thing apply for this config file as well?
I'm working on a web application at the moment with a REST API, database, rabbitMQ, a few python workers, nginx, 2 react projects and a Gatsby project plus some docker composes for testing.
Everything is configured in the root file and then this is imported into smaller dhall files in the subprojects. In the build scripts I generate all the yaml and JSON from these, even the docker-composes and it's very ergonomic.
At any rate the closest I could find to this saying was this HN post https://news.ycombinator.com/item?id=8092967 "Configuration files suck. Just use a programming language"
>but you could use the tool’s url as the key so it’s unique.
reminds me of XML namespaces.
on edit: formatting
I also don't think that the format matters more than it's use.
Reminds me of some good advice in The Pragmatic Programmer, should be (simple) text-files for editing and (regardless if one or multiple files), a single source of truth (e.g. if multiple files, not the same time the same data to edit in). This is roughly as I remember it and I found it always useful to apply.
If I see duplication, it's about fighting the rot and remove it.
If configuration files are generated, they don't belong under version control (there are some exclusions to this rule, e.g. pinning dependency versions for example when shared with the repository).
What an awful idea. I don't even know where to start enumerating the problems that this approach creates, and what for? I mean, just think about it. Have you ever had to comb through a config file to add support for something but while you were not sure what option dictated which and what impact this or that option had because documentation is ineffective or awful or even non-existent?
Now multiply that complexity by all the options added by all the tools expected to use a file, compounded by the fact that God knows how many tools also write to that file and may or may not stay in their lane when doing so.
Who in the hell wants to deal with that mess?
And what for? Just because someone wants to adopt a tool but has a problem with having to configure its own config file?
No, you're the only one arriving at that conclusion.
The whole point is that if you're going to adopt tooling then you'll need to configure them all, and that's fine with config files in the project root tree.
I’ll admit this is less of a problem in project dirs, since tooling usually confines itself to a single file. But tooling outputs often similarly pollute. For example, I wish Python dumped its artifacts in a single directory I could delete, instead of in every source subdir.
I think it's a fundamental difference in that the project repo should be self-contained and "owned", whereas arbitrary software I run really shouldn't be putting files in my home directory.
In a software repo, anything inside it belongs to the project.
Just like the gitlab-ci.yml, it handles things related to the whole repo, it basically runs a script in the root of the repo anyway.
Sure, supporting other paths is useful (eg. in case of a monorepo), but this default seems sane. Though I'm interested in where others move it, and why.
But nothing is fundamentally wrong either with XDG-esque directory structures.
.config/travis/
.ci/travis/
all the same to me, as long as it's consistent.
Because it defines the project's CICI pipeline? Aka how the project is built, tested, delivered, and deployed?
I mean, that's a more fundament part of the project than which files should be automatically ignored when committing stuff to git (i.e., .gitignore)
git clean has options to only clean ignored files, which generally does what you want. I could see there being weird situations where a file is .PRECIOUS (in make's terms) or where you have a file that's ignored but not a product of the build (a developer-specific config file?) where this doesn't work, but I haven't run into that yet, and it's freeing to not have to maintain a free target by hand.
It says
{ "compilerOptions": { "baseUrl": "./" }, "exclude": [ "node_modules" ] }
I go google what it is, seems to be some VSCode thing added automatically maybe? https://code.visualstudio.com/docs/languages/jsconfig
but VSCode thinks it should be marked red, something is wrong with this config file I have no familiarity with. grrr.
anyway - I think at some point, especially on projects with lots of devs and lots of tooling, you look at the root and like art you know what you like or don't like and sometimes there will be a feeling that there are just too many files here for configuring stuff! But probably that indicates some other underlying problem, not a problem with having config files in itself.
That feeling of mild anxiety in not knowing why these files are there can be useful, is all I'm saying.
And if you allow me to build on the "Finally" note in your comment with a personal note:
Some _human readable_ content belongs into the root as well, e.g. a read-me, information under which conditions the code can be used (copying info) and similar, the important stuff. Otherwise it would mean it only needs to be machine read-able (which can be fine, too, especially for pure configuration management, still though a repository will most likely checked out by a human from time to time).
Uncle Bob raises this question from time to time: When you checkout a repository and look into the tree, what do you see?
The root is always an opportunity to show the projects' birds-view.
Oh what a reminiscence to back then when exchanging software on floppy disks, and when the !readme.txt not written in your language, you found the "link" to the file in your "docs" folder often at the very top.
Or you just found the docs folder, in case no readme there.
Btw., this works on Github, too. The docs folder is normally directly visible above the fold while the read-me might not, especially when there is a long list of files which OP put on topic.
Recursive behavior would still work, the same way that Git can recognize that there is a ".git" directory in one of the ancestor directories.
Python packaging is a bit of a mess already, so when I recently started a new small package I wanted to choose tooling that would not clutter, but a fair amount of tool makers were reluctant to allow code into their repo that would use the unified toml rather than a ton of separate file.
.config is probably a better solution than a single top level toml, IMHO, but far more important is doing something unified rather than continuing the pollution of the top-level namespace, which merely obscures the project structure.
1. setup.py with self contained dependencies (this is my general preference)
2. setup.py loading from requirements.txt
3. Standalone requirements.txt
4. requirements.txt generated from requirements.in (or similar)
5. pyproject.toml + Poetry with a dependencies section
It’s also a tiny subset of what the parent describes.
I do sometimes feel that people are making rube goldberg machines out of their package management in an attempt to avoid just writing down all their deps.
Isn't that what pip freeze is for?
Python doesn't have the notion of dependencies.
Pip doesn't have dev dependencies, poetry does.
It's already available behind a feature flag.
https://pythoninsider.blogspot.com/2020/07/upgrade-pip-20-2-...
Oh and I forgot about setup.py. That too.
Please use something like Poetry to manage deps.
> An interesting side-effect of PEP 518 trying to introduce a standard file that all projects should (eventually) have is that non-build development tools realized they now had a file where they could put their own configuration. I say this is interesting because originally PEP 518 disallowed this, but people chose to ignore this part of the PEP xD We eventually updated the PEP to allow for this use-case since it became obvious people liked the idea of centralizing configuration data in a single file.
Hilarious and indeed interesting
All others stuff (CI/CD workflows, linters, formatters, code of conduct, funding, issue and PR automation, dependency bots, ...) moved to .github.
Now I have a clean and tidy Python project with a tiny 12-items root directory: https://github.com/kdeldycke/meta-package-manager
- .eslintrc.json to configure ESLint
- .prettierignore because it tries to format package-lock.json
- prettier.config.json
- npm/yarn and package.json + node_modules
- .nvmrc to pin your node version
- storybook
- renovate to update dependencies
- .editorconfig to ignore node_modules
- mocha for testing and .mochaarc.js
- stylelint and .stylelintrc.json + .stylelintignore
And maybe throw in Jest, tsconfig.json, a gulpfile, and some other stuff.
Hell, I thought Flask's root directory was getting a bit fluttery with .gitignore, .venv, .flaskenv, __pycache__, and requirements.txt...
Simplicity is a virtue (at least sometimes).
I mostly meant that I do all my greenfield JS dev using TypeScript. Out of curiosity, what bad patterns have you seen caused by adding types “post-hoc”?
Which part of JS, as a programming language, do you see as great or even adequate when compared with contemporary programming languages?
> Adding types post-hoc has never improved a codebase that I've seen but it has caused a lot more bad patterns to show up.
That assertion makes no sense. I mean, all TypeScript does is impose arbitrary sanity checks on a programming language which is plagued for not having any. Where do you interpret the addition of sanity checks as a source of problems?
Having lots of tools is, in some ways a good thing. That's how Unix was designed: have lots of little tools, each designed really well for its job. If you have a problem, and there is a tool for that problem, there's nothing wrong with using the tool to solve the problem. It may come with a learning curve, but what often happens without the tool is you end up evolving your own tool anyway, it's just not as well designed as the industry leader.
The problem described on this git issue is generally less of a problem on the Java projects I've worked on, since most of the equivalent configuration ends up in the pom.xml. The JS equivalent problem isn't a problem of too many tools in itself, but rather that no tool in JS has done what Maven has for Java.
There's lots to complain about in JavaScript land but IMO having a bunch of config files in the root is a feature, not a bug.
And no, splitting the Go part and the React part is not an option as it makes releasing ten times harder.
Telling people to "just use fewer tools" might make you feel clever, but it's just snarky, really.
Search path for config is:
* /etc/config
* $XDG_CONFIG_HOME (~/.config)
* $PROJECT_CONFIG ($PROJECT_ROOT/.config)
* directories between $PROJECT_ROOT and $CWD for .config
* $CWD/.config
This allows for system wide, then user wide, then project wide, then directory hierarchy configs, with lower levels overriding higher ones.
Inside .config, each tool can have it's own chosen file, directory, extensions directory, so:
.config/$TOOL.{json,yaml,toml,ini,xyz}
.config/$TOOL/config.ext
.config/$TOOL.d/modular config files eg 00-module.ext
Most of the time for most projects, there will be system wide and user wide config, then a project wide config. Overrides lower in the hierarchy tend to be less used.
Yes, it means that there's a tree search for a tool to get its final config. Whether that's a real issue for performance or otherwise is a potential to remove some of the hierarchy/search.
[1] https://specifications.freedesktop.org/basedir-spec/basedir-...
And yup, conventions like this work well Linux and BSD's. To note ~/.config for many configs
These days you see this happening inside of .github/, where configs related to gh repos and actions go.
If tooling authors started universally recognizing .config/ as a directory where we could keep stuff, the root could be super clean.
How about just one file, across languages / CI tools / etc? a "project.toml" or "project.yaml" or "project.json"?
[tool.npm]
name = "frontend"
version = "1.0.2"
[tool.npm.dependencies]
react = "^16.3.1"
[tool.travis-ci]
# ...
[tool.eslint]
# ...
[tool.poetry]
name = "backend"
[tool.poetry.dependencies]
django = "~3.1.0"
If vendors were willing to accept it, it'd be less clutter. Integration tools would only need to check one place (legacy configs would still have to be supported, though, so it's still a dream)Legacy configs could be ported via YAML / JSON straight into the TOML sections. It'd work for package.json or tslint.json, but not stuff involving runtime, e.g. .eslintrc.js
1. It supports real comments (so not json) 2. It supports imports/includes so if it gets unwieldy it can be split up.
Personally I like this approach way more than a bunch of if/else statements in a single file.
Sadly there's a confusing LocalLow and not all apps use AppData anyway. But it's something.
I never understood the difference between both. What is it!?
But opening the root project folder to just a list of subfolders gives me a very nice first impression.
Below is my list of config files for https://www.joyapp.com (Django + NodeJS). Not a hugely complex architecture, mind you.
Some notes: .editorconfig at least tries to consolidate this for some things. And pyproject.toml could help with Python. Still, definitely jarring and something I've noticed.
.coveragerc
.dockerignore
.editorconfig
.flake8
.gitattributes
.gitignore
.isort.cfg
.prettierrc
.shellcheckrc
jest.config.js
mypy.ini
package.json
pytest.ini
requirements.txt
shell.nix
tsconfig.json
tslint.json
webpack.config.ts
yarn.lockBut yeah, the most recent revision of the XDG basedir spec is dated November 2010; we've had it for a decade.
The holdouts fall into the following categories:
1. Projects where the author doesn't care but would take a PR.
2. "My tool is super simple and I don't want to complicate it to read env vars or have platform specific config"
3. Projects which value cross platform consistency over consistency with the platform (tmux, though the next version does budge a little)
4. Projects which consider XDG as just "some desktop Linux thing" and don't consider themselves as caring about desktop that much or just that they predate XDG (SSH)
5. Projects who mix runtime and config and cached data in a single directory and don't want to split them out for proper XDG support.
The ArchWiki probably has the best centralised tracking of attempts to get XDG support implemented: https://wiki.archlinux.org/index.php/XDG_Base_Directory
* Dependencies - Things required for your code to run, for example Dockerfile, package.json, yarn.lock
* Metrics - Measuring code quality, but the project works without these, such as jest.config.js, .eslintrc, .eslintignore
* Other - I don't recognize several config files here, and suspect some don't fall into the categories above, but am not sure. Also, README.md and .gitignore would fall into this category, but probably ought to remain at the top.
So instead of a generic "configs/" directory, if I was to organize these myself, I'd probably want at least two subdirectories so it would, y'know, actually be a bit more organized.
Let me organize my repository files however I want and however best fits my project organization.
Few pieces of development-related software frustrate me more than Visual studio.
The seeming disconnect between what's on disk, where on disk it is, and where and how it's displayed in the UI is cavernous. It also outright ignore things that are present in the folder unless you explicitly add it through the interface. God forbid you drop a file in, add it through the UI then move it because you discover you've dropped it into the wrong one of the 3 million nested folders large dotnet projects seem to generate.
Changing things on disk underneath it practically gives it an aneurysm. VSCode and IntelliJ IDE's handle the same situations without panicking, so why is VS so fragile?
This is no longer the case for "new style" VS project files (used by default on .Net Core projects). It works sanely now.
Some programs try to "correct" your input, some hide or even remove settings so they can't confuse users, some produce all kinds of popups in order to help but only obstructing the view of some information or messing up UI focus in the process, and the list goes on.
This is the best way to make me use other software whenever I have the choice.
for i in *But Windows has a lot of problems with hidden files, so a lot of people just set it to not hide any.
On most systems. There is a long-standing convention in the Unix-derived world that files and directories with names which start with a dot (this also includes "." and "..") are hidden in directory listings. The main exception is Windows, which inherited its conventions from MS-DOS; the convention there is that files and directories with the "hidden" and/or "system" attributes set are hidden from directory listings by default.
My .vimrc and .bashrc are super important, but I rarely need to edit them and I never need to be reminded of their existence, so having them show up every time I ran ls in home would be annoying.
The whole thing is a little uncomfortable though. There is a clear isomorphism between the contents of the structure of, say, a JSON file, and the contents of a directory of structured text files (and other directories). Heck, you could build a little tool with a slider that moves all the data between a single file, and a deep directory hierarchy with lots of tiny text files as the leaves.
Personally I feel like the best solution is to somehow "indelibly" combine the tool with its config within a project, de facto removing all config options from the developer. In the worst case, it could be something as silly as a wrapper script with config embedded in there. Of course this is a kind of fiction, but a useful one. It feels nice to get to a point where you don't have to think about tooling. (The last time for me was like 2000).
That's not a terrible idea. For me, I really like tools such as 'nft' that take advantage of the shebang line to solve more or less this exact problem.
If you have too many config files, then you should use less tools.
Besides, most of them are already hidden anyway (because of dotfiles).
If the main problem is that, when you go look at a project on github or gitlab, you see a long list of config files rather than the code you're looking for, a cleaner solution is for the git sites to show the listing for a /src directory instead of / on the project page.
Edit to add— someone suggests this on the linked issue, and it is mentioned as a concern that auto-hiding the files could lead to them being used to conceal malicious code. Not sure how significant of a concern that really is, but it's an interesting angle, anyway.
GitHub and other tools should do what ls does and hide dotfiles by default. That’s why they are dotfiles.
Other project-unrelated config/metadata should follow the .git example and use dotfile config files, preferably yaml or something similar (not json, as it does not support comments).
Basically, if you want to add a new "tool" to your project, you add it as a plugin in `build.gradle`:
plugins {
id "com.github.spotbugs" version "4.5.0"
}
Then, in the same file, you configure it: spotbugs {
visitors = [ 'FindSqlInjection', 'SwitchFallthrough' ]
// more config
}
Some plugins require external files, but that's usually because the plugin was not designed for Gradle and/or the authors didn't bother to add some code in their plugins to read config from the project file (which is usually very easy).Though XML solved the issue of needing multiple config files long ago, with namespaces.
In which case you probably have enough free time to build a tool that just moves files around at runtime. Organize your repo however you want, then run your tool as a wrapper for any other command, and it'll move all files into the places other tools expect them. Now you've solved the problem, which was that humans like aesthetically pleasing things that serve no useful purpose.
Additionally, your OS probably already natively hides files that start with a dot, so this is just a UI problem.
Please don’t “solve” this issue by moving files to a sub-directory. If anything, only leave non-config files there, it’s the obvious simple solution that most projects follow anyway.
I love the idea of a standard config file, but... Cross-project standardization in the JS ecosystem? Unlikely.
I know some tools allow you to specify the location of your config files, and it would be nice if all tools started to do that.
When you use a terminal you don't really care how many files are in a directory, unless they have names that are annoying to autocomplete.
The reason we bother with modules and directories is to help organize code and ease discovery. And whether we use ls or a GUI, we all start out the process of discovery with a directory listing.
The top-level directory, where these files are, is often where there's the most flexibility in terms of how to lay out things. All this clutter hides the project organization and makes it difficult to discover what's going on. It was one thing when it was just autotools, but the proliferation of tools in the last decade has brought it over a tipping point, IMHO.
It is the case: They don't bother me when I'm using a CLI, but when I use Visual Studio Code for instance, all those files are annoying when I'm looking for something/scrolling past them.
If it's about "cleaning up" before committing, a single script that moves files back and forth from project root to a .config path might also be an option...
if your project is a hodgepodge of tooling the problem is the hodgepodge of tooling in your project, maybe you need to nest modules or to split libraries, instead of changing everyone else sane defaults because modern hip toolchains can't handle dependents and nested subprojects without stepping on each other toes or requiring the full lib in a single place
I think our tools drive this aversion quite a bit.
I do like the .config directory naming suggestion though.
To me the scourge is tool config files being in the repo at all.
Ok I’ll make an exception for .gitignore or something that enforces house style or formatting conventions to make things proper for check in.
But an editor config? That’s so user-specific and shouldn’t affect (or should I say “infect”) the source code.
> Why don’t you move all /usr contents to / and forget about /usr?
> Because this introduces a lot of new toplevel directories, which all have to be mount points then to be shared across other hosts.
> Ok, but what about a root filesystem on the network and mounting local filesystems only?
> Then you would share the toplevel directory hierarchy among all hosts. Hosts would need to mount /etc and /var for host-only versions.
So it is about network boot. Sad. There is nothing "usr" about /usr, it is system installed packages - /sys or /System. And /usr is for "/usr/dmr" which is /home. At least no more /usr/local.
Put everything in a folder, and use a namespace, e.g.:
org.organization-name.project.json