# Csproj Modernizer > Audit MSBuild project and build files for anti-patterns, and modernize legacy .NET Framework projects to SDK-style. The free check runs in the browser and uploads nothing: it parses every pasted .csproj, .vbproj, .fsproj, Directory.Build.props, Directory.Build.targets, Directory.Packages.props, other .props/.targets file and packages.config with line numbers and runs a deterministic subset of the dotnet/skills msbuild-antipatterns catalogue, plus cross-file checks and a legacy inventory. The paid audit confirms or dismisses every flag and gives a BAD-to-GOOD fix per finding; the paid modernize run rewrites a legacy project to SDK-style. The page checks every quote, line number, fix and kept package, reference, item, property and target against the files. App: https://csproj-modernizer.skillsafe.ai/ API tutorial: https://csproj-modernizer.skillsafe.ai/api.html ## Input - One or more MSBuild files pasted into one box. Each file starts at a marker line `` (dropping files adds the markers); `==> path <==` as printed by head/tail also works. Without a marker the paste is one file, named from its content. - Nothing runs MSBuild, restores packages or resolves imports: every flag is a reading of the text, with the file and line it points at. - Cross-file checks see only what is pasted: paste Directory.Build.props/.targets with the projects to catch a .targets file overriding a project, and a referenced project to confirm a SetTargetFramework race. ## Flags the free check raises - AP-01 (low): for an operation that has a built-in task. - AP-02 (high): Unquoted condition comparison. - AP-03 (high): Hardcoded absolute path. - AP-04 (low): Restating an SDK default. - AP-05 (low): Manual file listing in an SDK-style project. - AP-06 (medium): with a HintPath into packages/. - AP-07 (medium): Analyzer or build-tool package without PrivateAssets="all". - AP-08 (low): The same properties copy-pasted across projects. - AP-09 (low): Package versions scattered without central management. - AP-10 (low): Monolithic target. - AP-11 (medium): Custom target without Inputs and Outputs. - AP-12 (low): Property defaults set in a .targets file. - AP-13 (medium): Import without an Exists() guard. - AP-14 (high): Backslash path in a raw shell string. - AP-15 (low): The same property set unconditionally in two scopes. - AP-16 (low): for string or path operations. - AP-17 (low): Include and Update of one item type in one ItemGroup. - AP-18 (low): Redundant ProjectReference to a transitive project. - AP-19 (high): Side effect during property evaluation. - AP-20 (low): Platform-specific without an OS condition. - AP-21 (high): Property conditioned on $(TargetFramework) in a .props file. - AP-22 (high): task forking a project with path-neutral global properties. - AP-23 (high): SetTargetFramework re-injecting a single-targeting project's own TFM. - MX-01 (medium): $(SolutionDir) in a project file. - MX-02 (medium): DefineConstants overwritten instead of appended. - MX-03 (low): LangVersion latest or preview. - MX-04 (high): Central package management with a Version on PackageReference (NU1008). - MX-05 (high): BeforeBuild / AfterBuild target overridden in an SDK-style project. - MX-06 (medium): TargetFramework and TargetFrameworks both set. - LG-01 (legacy marker): Legacy (non-SDK) project format. - LG-02 (legacy marker): Explicit Compile list in a legacy project. - LG-03 (legacy marker): Properties/AssemblyInfo.cs compiled. - LG-04 (legacy marker): packages.config package references. - LG-05 (legacy marker): Framework references the SDK adds implicitly. - LG-06 (legacy marker): Per-configuration boilerplate property groups. - LG-07 (legacy marker): ASP.NET (System.Web) web application project. - XML-01 (high): The file is not well-formed XML. Severity: high = the catalogue's error band (breaks or silently miscompiles the build), medium = warning (fragile or leaks), low = style (noise, maintainability). A few rules adjust the band per instance: AP-05 is high because an explicit Compile of a globbed file fails with NETSDK1022; AP-07 is low in a test project; AP-12 is medium where a pasted project's own value is being overridden; AP-23 is low when the referenced project was not pasted. ## The legacy inventory For each legacy (non-SDK) project the page lists what an SDK-style rewrite must keep: the target framework (TargetFrameworkVersion mapped, e.g. v4.8 -> net48), output type, assembly name and root namespace when they differ from the file name, every package from packages.config or HintPath references with its version, project references, non-implicit assembly references, Content/None/EmbeddedResource items with CopyToOutputDirectory, Link and similar metadata, every property that is not SDK boilerplate, DefineConstants beyond DEBUG/TRACE, custom targets, BeforeBuild/AfterBuild overrides that must be renamed, AssemblyInfo.cs and the F# compile order. ## Paid lanes - `audit`: verdict broken | needs_work | clean; findings with catalogue id, the page flags confirmed, file, line, severity (error | warning | style), title, verbatim evidence, why, the BAD XML and the GOOD XML; one answer (confirmed | dismissed with a reason) per page flag; an ordered plan; whether to modernize first. - `modernize`: verdict converted | already_sdk | cannot_convert; the project, target framework (and a reason if retargeted), the files (rewrite | create | delete, full content), what was removed and kept and why, manual steps and risks. An ASP.NET System.Web web application has no Microsoft SDK-style equivalent on .NET Framework, so it is cannot_convert with the options explained. - Handoffs: an audit of a legacy project offers the modernize lane on the same paste; a rewrite can be loaded back into the paste and audited. ## What the page checks in each reply - Audit: every finding names a pasted file and a line inside it; its evidence and BAD snippet are really in that file (whitespace-insensitive); its GOOD snippet parses as XML, has no unquoted condition and, for AP-03, no absolute path; every high, medium and low flag is answered exactly once and consistently with the findings; the verdict fits the severities. - Modernize: the rewrite parses; is SDK-style (Sdk attribute, no ToolsVersion, no legacy imports); keeps the target framework, output type and names; keeps every package at its version, every project and assembly reference, every item with its metadata (an Update of an item the SDK does not create is caught), every property, constant and target or says why not; renames BeforeBuild/AfterBuild; handles AssemblyInfo; keeps the F# order; lists no globbed Compile items; and passes the free check again with no high flag. ## Sources - msbuild-antipatterns, dotnet/skills (github.com/dotnet/skills, plugins/dotnet-msbuild/skills/msbuild-antipatterns), MIT - https://skillsafe.ai/skill/@dotnet/msbuild-antipatterns - msbuild-modernization, dotnet/skills (plugins/dotnet-msbuild/skills/msbuild-modernization), MIT - https://skillsafe.ai/skill/@dotnet/msbuild-modernization - MSBuild documentation (learn.microsoft.com/visualstudio/msbuild) and NuGet Central Package Management (learn.microsoft.com/nuget/consume-packages/central-package-management) for the NETSDK1022, NU1008 and evaluation-order facts the catalogue relies on. Model: gpt-terra, metered per run; the free check needs no account. Csproj Modernizer is an independent tool, not affiliated with or endorsed by Microsoft. MSBuild and .NET are Microsoft trademarks, named only to say which files it reads.