Everytime I setup a new project, besides consideration on if I need microservice architecture or a good old monolithic application there are always simliar steps needed to get the project in a maintainable state.
SCM Github is my goto but other exist like GitLab and Bitbucket
Files
Docker Compose Regardless of the architecture, anything new needs to run in a container. This is just defacto now days. I built a simple demo a few years back which is still relavant. Today I’d add that compose locally should bring up a containerised local environment for everything your application needs to run.
This should be run from one file like Run_Local.ps1
, below is an example.
Example Run_Local.ps1
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 param ( [Parameter ()] [switch ] $RebuildStubs , [Parameter ()] [switch ] $NoBff , [Parameter ()] [switch ] $LocalStackOnly ) Set-StrictMode -Version 2.0 $ErrorActionPreference = 'Stop' Write-Output '****** Running stubs ******' $host .ui.RawUI.WindowTitle = 'Porky Stubs' if ($LocalStackOnly .isPresent) { Write-Output '****** STARTING ONLY LOCALSTACK ******' docker start pky_localstack } elseif ($NoBff .isPresent) { Write-Output '****** NO BFF ******' docker-compose --project-name porky -f ./stubs/docker-compose-no-bff .yml up ($RebuildStubs ? "--build" : "" ) --remove-orphans } else { Write-Output 'No params found! Try -LocalStackOnly or -NoBff and optionally -RebuildStubs' }
Code Conventions We want all members of the team rowing in the same direction. As there are many IDEs (Visual Studio, Ryder ect) the simplest way is to commit a .editorconfig
file with the source control. Check out https://editorconfig.org/
Works out of the box with some, for VS Code you need a plugin. (Also see IDE Visual Studio Code )
Also see Rule categories , these are for the dotnet_diagnostic.x
records.
Example .editorconfig (From Milan Jovanovics Clean Architecture example)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 root = true # C# files [*.cs] #### Core EditorConfig Options #### # Indentation and spacing indent_size = 4 indent_style = space tab_width = 4 # New line preferences end_of_line = crlf insert_final_newline = true #### .NET Coding Conventions #### # Organize usings dotnet_separate_import_directive_groups = false dotnet_sort_system_directives_first = true # this. and Me. preferences dotnet_style_qualification_for_event = false:error dotnet_style_qualification_for_field = false:error dotnet_style_qualification_for_method = false:error dotnet_style_qualification_for_property = false:error # Language keywords vs BCL types preferences dotnet_style_predefined_type_for_locals_parameters_members = true:error dotnet_style_predefined_type_for_member_access = true:error # Parentheses preferences dotnet_style_parentheses_in_arithmetic_binary_operators = never_if_unnecessary:error dotnet_style_parentheses_in_other_binary_operators = never_if_unnecessary:error dotnet_style_parentheses_in_other_operators = never_if_unnecessary:error dotnet_style_parentheses_in_relational_binary_operators = never_if_unnecessary:error # Modifier preferences dotnet_style_require_accessibility_modifiers = for_non_interface_members:error # Expression-level preferences dotnet_style_coalesce_expression = true:error dotnet_style_collection_initializer = true:error dotnet_style_explicit_tuple_names = true:error dotnet_style_null_propagation = true:error dotnet_style_object_initializer = true:error dotnet_style_prefer_auto_properties = true:warning dotnet_style_prefer_compound_assignment = true:error dotnet_style_prefer_conditional_expression_over_assignment = true:error dotnet_style_prefer_conditional_expression_over_return = true:none dotnet_style_prefer_inferred_anonymous_type_member_names = true:error dotnet_style_prefer_inferred_tuple_names = true:error dotnet_style_prefer_is_null_check_over_reference_equality_method = true:error csharp_indent_labels = one_less_than_current csharp_using_directive_placement = outside_namespace:error csharp_prefer_simple_using_statement = true:error csharp_prefer_braces = true:error csharp_style_namespace_declarations = file_scoped:error csharp_style_prefer_method_group_conversion = true:silent csharp_style_prefer_top_level_statements = true:silent csharp_style_prefer_primary_constructors = true:none csharp_style_expression_bodied_methods = false:silent csharp_style_expression_bodied_constructors = false:silent csharp_style_expression_bodied_operators = true:error csharp_style_expression_bodied_properties = true:error csharp_style_expression_bodied_indexers = true:error csharp_style_expression_bodied_accessors = true:error csharp_style_expression_bodied_lambdas = true:error csharp_style_expression_bodied_local_functions = true:error [*.{cs,vb}] dotnet_style_prefer_simplified_boolean_expressions = true:suggestion dotnet_style_prefer_simplified_interpolation = true:suggestion dotnet_style_namespace_match_folder = true:suggestion # Field preferences dotnet_style_readonly_field = true:error # Parameter preferences dotnet_code_quality_unused_parameters = all:none #### C# Coding Conventions #### # Namespace preferences csharp_style_namespace_declarations= file_scoped:error # var preferences csharp_style_var_elsewhere = false:error csharp_style_var_for_built_in_types = false:error csharp_style_var_when_type_is_apparent = true:error # Expression-bodied members csharp_style_expression_bodied_accessors = true:error csharp_style_expression_bodied_constructors = false:silent csharp_style_expression_bodied_indexers = true:error csharp_style_expression_bodied_lambdas = true:error csharp_style_expression_bodied_local_functions = true:error csharp_style_expression_bodied_methods = false:silent csharp_style_expression_bodied_operators = true:error csharp_style_expression_bodied_properties = true:error # Pattern matching preferences csharp_style_pattern_matching_over_as_with_null_check = true:error csharp_style_pattern_matching_over_is_with_cast_check = true:error csharp_style_prefer_switch_expression = true:error # Null-checking preferences csharp_style_conditional_delegate_call = true:error # Modifier preferences csharp_prefer_static_local_function = true:error csharp_preferred_modifier_order = public,private,protected,internal,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,volatile,async # Code-block preferences csharp_prefer_braces = true:error csharp_prefer_simple_using_statement = true:error # Expression-level preferences csharp_prefer_simple_default_expression = true:error csharp_style_deconstructed_variable_declaration = true:suggestion csharp_style_inlined_variable_declaration = true:error csharp_style_pattern_local_over_anonymous_function = true:error csharp_style_prefer_index_operator = true:suggestion csharp_style_prefer_range_operator = true:suggestion csharp_style_throw_expression = true:suggestion csharp_style_unused_value_assignment_preference = discard_variable:silent csharp_style_unused_value_expression_statement_preference = discard_variable:none csharp_style_prefer_method_group_conversion = true:silent csharp_style_prefer_top_level_statements = true:silent # 'using' directive preferences csharp_using_directive_placement = outside_namespace:error #### C# Formatting Rules #### # New line preferences csharp_new_line_before_catch =true csharp_new_line_before_else =true csharp_new_line_before_finally =true csharp_new_line_before_members_in_anonymous_types = false, csharp_new_line_before_members_in_object_initializers = false, csharp_new_line_before_open_brace = all csharp_new_line_between_query_expression_clauses = true # Indentation preferences csharp_indent_block_contents = true csharp_indent_braces = false csharp_indent_case_contents = true csharp_indent_case_contents_when_block = true csharp_indent_labels = one_less_than_current csharp_indent_switch_labels = true # Space preferences csharp_space_after_cast = false csharp_space_after_colon_in_inheritance_clause = true csharp_space_after_comma = true csharp_space_after_dot = false csharp_space_after_keywords_in_control_flow_statements = true csharp_space_after_semicolon_in_for_statement = true csharp_space_around_binary_operators = before_and_after csharp_space_around_declaration_statements = false csharp_space_before_colon_in_inheritance_clause = true csharp_space_before_comma = false csharp_space_before_dot = false csharp_space_before_open_square_brackets = false csharp_space_before_semicolon_in_for_statement = false csharp_space_between_empty_square_brackets = false csharp_space_between_method_call_empty_parameter_list_parentheses = false csharp_space_between_method_call_name_and_opening_parenthesis = false csharp_space_between_method_call_parameter_list_parentheses = false csharp_space_between_method_declaration_empty_parameter_list_parentheses = false csharp_space_between_method_declaration_name_and_open_parenthesis = false csharp_space_between_method_declaration_parameter_list_parentheses = false csharp_space_between_parentheses = false csharp_space_between_square_brackets = false # Wrapping preferences csharp_preserve_single_line_blocks = true csharp_preserve_single_line_statements = false #### Naming styles #### # Naming rules dotnet_naming_rule.interface_should_be_begins_with_i.severity = suggestion dotnet_naming_rule.interface_should_be_begins_with_i.symbols = interface dotnet_naming_rule.interface_should_be_begins_with_i.style = begins_with_i dotnet_naming_rule.types_should_be_pascal_case.severity = suggestion dotnet_naming_rule.types_should_be_pascal_case.symbols = types dotnet_naming_rule.types_should_be_pascal_case.style = pascal_case dotnet_naming_rule.non_field_members_should_be_pascal_case.severity = suggestion dotnet_naming_rule.non_field_members_should_be_pascal_case.symbols = non_field_members dotnet_naming_rule.non_field_members_should_be_pascal_case.style = pascal_case # Symbol specifications dotnet_naming_symbols.interface.applicable_kinds = interface dotnet_naming_symbols.interface.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected dotnet_naming_symbols.interface.required_modifiers = dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum dotnet_naming_symbols.types.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected dotnet_naming_symbols.types.required_modifiers = dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected dotnet_naming_symbols.non_field_members.required_modifiers = # Naming styles dotnet_naming_style.pascal_case.required_prefix = dotnet_naming_style.pascal_case.required_suffix = dotnet_naming_style.pascal_case.word_separator = dotnet_naming_style.pascal_case.capitalization = pascal_case dotnet_naming_style.begins_with_i.required_prefix = I dotnet_naming_style.begins_with_i.required_suffix = dotnet_naming_style.begins_with_i.word_separator = dotnet_naming_style.begins_with_i.capitalization = pascal_case # Custom Rules - configure these as required # .NET Code Analyzers rules # CA1000: Do not declare static members on generic types dotnet_diagnostic.CA1000.severity = none # CA1002: Do not expose generic lists dotnet_diagnostic.CA1002.severity = none # CA1008: Enums should have zero value dotnet_diagnostic.CA1008.severity = none # CA1019: Define accessors for attribute arguments dotnet_diagnostic.CA1019.severity = none # CA1024: Use properties where appropriate dotnet_diagnostic.CA1024.severity = none # CA1030: Use events where appropriate dotnet_diagnostic.CA1030.severity = none # CA1031: Do not catch general exception types dotnet_diagnostic.CA1031.severity = none # CA1032: Implement standard exception constructors dotnet_diagnostic.CA1032.severity = none # CA1034: Nested types should not be visible dotnet_diagnostic.CA1034.severity = none # CA1040: Avoid empty interfaces dotnet_diagnostic.CA1040.severity = none # CA1051: Do not declare visible instance fields dotnet_diagnostic.CA1051.severity = none # CA1056: URI-like properties should not be strings dotnet_diagnostic.CA1056.severity = none # CA1062: Validate arguments of public methods dotnet_diagnostic.CA1062.severity = none # CA1063: Implement IDisposable Correctly dotnet_diagnostic.CA1063.severity = none # CA1307: Specify StringComparison for clarity dotnet_diagnostic.CA1307.severity = none # CA1515: Consider making public types internal dotnet_diagnostic.CA1515.severity = none # CA1700: Do not name enum values 'Reserved' dotnet_diagnostic.CA1700.severity = none # CA1707: Identifiers should not contain underscores dotnet_diagnostic.CA1707.severity = none # CA1711: Identifiers should not have incorrect suffix dotnet_diagnostic.CA1711.severity = none # CA1716: Identifiers should not match keywords dotnet_diagnostic.CA1716.severity = none # CA1724: Type names should not match namespaces dotnet_diagnostic.CA1724.severity = none # CA1725: Parameter names should match base declaration dotnet_diagnostic.CA1725.severity = none # CA1812: Avoid uninstantiated internal classes dotnet_diagnostic.CA1812.severity = none # CA1816: Dispose methods should call SuppressFinalize dotnet_diagnostic.CA1816.severity = none # CA1819: Properties should not return arrays dotnet_diagnostic.CA1819.severity = none # CA1822: Mark members as static dotnet_diagnostic.CA1822.severity = none # CA1848: Use the LoggerMessage delegates dotnet_diagnostic.CA1848.severity = none # CA1860: Avoid using 'Enumerable.Any()' extension method dotnet_diagnostic.CA1860.severity = none # CA2007: Consider calling ConfigureAwait on the awaited task dotnet_diagnostic.CA2007.severity = none # CA2201: Do not raise reserved exception types dotnet_diagnostic.CA2201.severity = none # CA2211: Non-constant fields should not be visible dotnet_diagnostic.CA2211.severity = none # CA2213: Disposable fields should be disposed dotnet_diagnostic.CA2213.severity = none # CA2225: Operator overloads have named alternates dotnet_diagnostic.CA2225.severity = none # CA2227: Collection properties should be read only dotnet_diagnostic.CA2227.severity = none # CA2234: Pass system uri objects instead of strings dotnet_diagnostic.CA2234.severity = none # CA2326: Do not use TypeNameHandling values other than None dotnet_diagnostic.CA2326.severity = none # CA2326: Do not use insecure JsonSerializerSettings dotnet_diagnostic.CA2327.severity = none # CS8600: Converting null literal or possible null value to non-nullable type. dotnet_diagnostic.CS8600.severity = none # CS8603: Possible null reference return. dotnet_diagnostic.CS8603.severity = none # CS8618: Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable. dotnet_diagnostic.CS8618.severity = none # IDE Code Analyzers rules # IDE0005 dotnet_diagnostic.IDE0005.severity = none # IDE0046: Convert to conditional expression dotnet_diagnostic.IDE0046.severity = none # IDE0058: Expression value is never used dotnet_diagnostic.IDE0058.severity = none # IDE0060: Remove unused parameter dotnet_diagnostic.IDE0060.severity = none # IDE0072: Add missing cases dotnet_diagnostic.IDE0072.severity = none # IDE0290: Use primary constructor dotnet_diagnostic.IDE0290.severity = none # SonarAnalyzer.CSharp rules # S112: General or reserved exceptions should never be thrown dotnet_diagnostic.S112.severity = none # S125: Sections of code should not be commented out dotnet_diagnostic.S125.severity = none # S1135: Track uses of "TODO" tags dotnet_diagnostic.S1135.severity = none # S2094: Utility classes should not have public constructors dotnet_diagnostic.S1118.severity = none # S2094: Classes should not be empty dotnet_diagnostic.S2094.severity = none # S2325: Methods and properties that don't access instance data should be static dotnet_diagnostic.S2325.severity = none # S2365: Properties should not make collection or array copies dotnet_diagnostic.S2365.severity = none # S3267: Loops should be simplified with "LINQ" expressions dotnet_diagnostic.S3267.severity = none # S3881: "IDisposable" should be implemented correctly dotnet_diagnostic.S3881.severity = none # S4136: Method overloads should be grouped together dotnet_diagnostic.S4136.severity = none # S4158: Empty collections should not be accessed or iterated dotnet_diagnostic.S4158.severity = none # S6605: Collection-specific "Exists" method should be used instead of the "Any" extension dotnet_diagnostic.S6605.severity = none # S6781: JWT secret keys should not be disclosed dotnet_diagnostic.S6781.severity = none
Static Code Analysis You have more than one option!
SonarScanner (Remote Server) Its important to understand that sonarscanner doesnt run tests, it reports on static code and can be provided test coverage reports.
Run your own containerised Sonarqube server locally.
SonarAnalyzer (Local Static Code Analysis) Although many large IT companies have a sonar server which can be used a a quality gate in PRs and Master builds its possible to do these checks locally.
Unit Test code coverage
Logging
CI/CD pipeline CI/CD pipeline for build & tests
References