Problem
Static classes across the codebase mix fixed values with behaviour. The name says data, the contents include lookups, file-system probes and I/O. Callers cannot tell from the call site which one they are getting.
Constants was the worst case and is the reason this issue exists. It held ~31 const values and ~19 static methods that walk parent directories, probe Directory.Exists, and read EnvironmentInfo. Splitting it (#586) took a day of back-and-forth precisely because nobody could answer "can this move?" without reading every member — the values could move to Fallout.Core, the behaviour could not.
Others with the same shape, found by scanning for static classes carrying both const fields and static methods:
| Class |
consts |
methods |
PathConstruction (Fallout.Utilities/IO) |
3 |
25 |
ReflectionUtility (Fallout.Utilities/Reflection) |
3 |
16 |
Logging (Fallout.Build) |
3 |
12 |
EncryptionUtility (Fallout.Utilities/Security) |
10 |
6 |
SignPathTasks (Fallout.Common/Tools/SignPath) |
7 |
6 |
PlatformNames (Fallout.Persistence.Solution) |
12 |
3 |
Not every one of these is wrong — a utility class of related functions is fine. The problem is that there is no rule, so "static class" is used for constants, for pure functions, and for file-system and network work alike.
The cost shows up whenever a type needs to move between layers. A class of pure values can sit in the innermost project; anything touching I/O cannot. Today that has to be worked out member by member, every time.
Outcome
A written rule separating the two, and the worst offenders split to match it. Whether a static class can move inward is answerable from its name and its dependencies, not by reading every member.
Acceptance criteria
Notes
Problem
Static classes across the codebase mix fixed values with behaviour. The name says data, the contents include lookups, file-system probes and I/O. Callers cannot tell from the call site which one they are getting.
Constantswas the worst case and is the reason this issue exists. It held ~31constvalues and ~19 static methods that walk parent directories, probeDirectory.Exists, and readEnvironmentInfo. Splitting it (#586) took a day of back-and-forth precisely because nobody could answer "can this move?" without reading every member — the values could move toFallout.Core, the behaviour could not.Others with the same shape, found by scanning for static classes carrying both
constfields and static methods:PathConstruction(Fallout.Utilities/IO)ReflectionUtility(Fallout.Utilities/Reflection)Logging(Fallout.Build)EncryptionUtility(Fallout.Utilities/Security)SignPathTasks(Fallout.Common/Tools/SignPath)PlatformNames(Fallout.Persistence.Solution)Not every one of these is wrong — a utility class of related functions is fine. The problem is that there is no rule, so "static class" is used for constants, for pure functions, and for file-system and network work alike.
The cost shows up whenever a type needs to move between layers. A class of pure values can sit in the innermost project; anything touching I/O cannot. Today that has to be worked out member by member, every time.
Outcome
A written rule separating the two, and the worst offenders split to match it. Whether a static class can move inward is answerable from its name and its dependencies, not by reading every member.
Acceptance criteria
Notes
Constants→Fallout.Core.Constants(values) +Fallout.Build.Shared.FalloutPaths(behaviour). Use it as the worked example.