Skip to content

Commit 2d7e417

Browse files
authored
Update styleguide.md
1 parent ac8d9b2 commit 2d7e417

File tree

1 file changed

+248
-0
lines changed

1 file changed

+248
-0
lines changed

.gemini/styleguide.md

Lines changed: 248 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,249 @@
1+
Company Betagro C# Style Guide
12

3+
Introduction
4+
This style guide outlines the coding conventions for C# code developed at Company Betagro. It's based on standard C# conventions and best practices, particularly those recommended by Microsoft, with some modifications to address specific needs and preferences within our organization.
5+
6+
Key Principles
7+
8+
Readability: Code should be easy to understand for all team members. Use clear naming and logical structure.
9+
Maintainability: Code should be easy to modify and extend. Follow SOLID principles and established design patterns where appropriate.
10+
Consistency: Adhering to a consistent style across all projects improves collaboration and reduces errors. Utilize tooling to enforce consistency.
11+
Performance: While readability is paramount, code should be efficient. Be mindful of performance implications, especially in critical paths.
12+
Deviations from Common C# Conventions (if any - explicitly mention them here)
13+
14+
(This section is for noting any specific deviations Betagro chooses. The original Java guide only noted line length, which is less strictly defined in C# anyway)
15+
## Line Length
16+
* Maximum line length: 100 characters (aligning with the original guide's preference).
17+
* While C# doesn't have a strict official limit, and tools often default to 120, we aim for 100 characters to maintain consistency where practical.
18+
* Readability is the primary goal; longer lines are acceptable if they significantly improve clarity (e.g., LINQ queries, long strings, fluent configurations).
19+
20+
## Indentation
21+
* Use 4 spaces per indentation level. (Standard C# convention)
22+
* Do not use tabs for indentation. Configure IDEs accordingly.
23+
24+
## Naming Conventions
25+
* Local Variables & Parameters: Use camelCase starting with a lowercase letter: userName, totalCount.
26+
* Constants (const, static readonly): Use PascalCase: MaxValue, DatabaseName. Avoid underscores.
27+
* For private const or private static readonly fields intended only for internal class use, _camelCase might be acceptable if consistent within the project, but PascalCase is generally preferred.
28+
* Methods (including local functions): Use PascalCase: CalculateTotal(), ProcessData().
29+
* Classes, Structs, Enums, Delegates, Records: Use PascalCase: UserManager, PaymentProcessor, OrderStatus.
30+
* Interfaces: Use PascalCase prefixed with I: IUserService, IPaymentGateway.
31+
* Properties: Use PascalCase: CustomerName, IsEnabled.
32+
* Namespaces: Use PascalCase, typically reflecting the assembly structure: CompanyBetagro.Utilities, CompanyBetagro.Data.Repositories. Avoid underscores.
33+
* Acronyms: Treat acronyms like words in PascalCase and camelCase (e.g., XmlReader, htmlDocument), except for two-letter acronyms which should be uppercase (e.g., IOStream).
34+
35+
## XML Documentation Comments
36+
* Use /// <summary>...</summary> for all public/internal/protected types and members (classes, interfaces, methods, properties, fields, enums, delegates).
37+
* First line of <summary>: Concise summary of the element's purpose, ending with a period.
38+
* For complex methods/members: Include detailed descriptions in the <summary> or use <remarks>. Document parameters (<param name="name">), return values (<returns>), exceptions (<exception cref="Type">), and type parameters (<typeparam name="name">).
39+
* Follow standard XML documentation comment conventions.
40+
41+
C#
42+
43+
/// <summary>
44+
/// Single-line summary describing the method's purpose.
45+
/// </summary>
46+
/// <param name="param1">Description of the first parameter.</param>
47+
/// <param name="param2">Description of the second parameter.</param>
48+
/// <returns>Description of the return value. E.g., True for success, False otherwise.</returns>
49+
/// <exception cref="ArgumentNullException">Thrown when <paramref name="param2"/> is null.</exception>
50+
/// <exception cref="ArgumentException">Thrown when <paramref name="param2"/> is invalid.</exception>
51+
/// <remarks>
52+
/// More detailed description or usage notes, if necessary.
53+
/// </remarks>
54+
public bool MyMethod(int param1, string param2)
55+
{
56+
if (param2 == null)
57+
{
58+
throw new ArgumentNullException(nameof(param2));
59+
}
60+
if (string.IsNullOrWhiteSpace(param2))
61+
{
62+
throw new ArgumentException("Parameter cannot be empty.", nameof(param2));
63+
}
64+
// method body here
65+
return true; // Example return
66+
}
67+
## Generics
68+
69+
Utilize generics where appropriate to improve type safety and code reusability.
70+
Use descriptive names for type parameters (e.g., TKey, TValue, TEntity) or a single T if the context is obvious.
71+
## Comments
72+
73+
Write clear and concise comments using // for single-line or end-of-line comments. Use /* ... */ for temporarily commenting out blocks of code (avoid committing this).
74+
Use /// XML comments for documenting APIs (as described above).
75+
Explain the why behind non-obvious code, not just the what.
76+
Comment sparingly: Well-named variables, methods, and classes should make the code largely self-documenting.
77+
Avoid commented-out code in the final commit. Use source control history instead.
78+
## Logging
79+
80+
Use a standard logging framework: Company Betagro uses [Specify framework, e.g., Microsoft.Extensions.Logging with Serilog/NLog, Serilog directly].
81+
Inject logger instances (e.g., ILogger<T>) via dependency injection where possible.
82+
Log at appropriate levels: Trace, Debug, Information, Warning, Error, Critical.
83+
Provide context: Include relevant information (e.g., IDs, method names, key parameters) in log messages using structured logging placeholders (e.g., LogInformation("Processing order {OrderId} for customer {CustomerId}", orderId, customerId);).
84+
Avoid logging sensitive information like passwords or PII unless properly masked or required by specific security protocols.
85+
## Error Handling
86+
87+
Use specific exception types derived from System.Exception. Avoid catching System.Exception or System.SystemException unless re-throwing or at the top level for logging.
88+
Throw exceptions for exceptional conditions, not for normal control flow.
89+
Handle exceptions gracefully: Use try...catch blocks. Catch specific exceptions you can handle meaningfully. Let others propagate up.
90+
Use try...finally or preferably using statements for deterministic cleanup of resources (IDisposable objects like streams, database connections, etc.).
91+
Provide informative error messages in exceptions. Include relevant context where possible.
92+
Consider using the Result pattern or similar constructs for operations where failure is a common, expected outcome (instead of throwing exceptions).
93+
## Tooling
94+
95+
Code formatter: Utilize the formatting capabilities built into Visual Studio, VS Code, or JetBrains Rider. Ensure consistent settings across the team, potentially managed via .editorconfig.
96+
Static Analysis / Linting: Leverage Roslyn Analyzers (included with the .NET SDK and enhanced by installing additional analyzer packages like StyleCop.Analyzers, SonarAnalyzer.CSharp, etc.). Configure rule severity via .editorconfig or ruleset files.
97+
Team-wide Settings: Use .editorconfig files checked into source control to define and enforce many formatting and code style rules consistently across different IDEs.
98+
## Example
99+
100+
C#
101+
102+
using System;
103+
using System.Security.Cryptography;
104+
using System.Text;
105+
using Microsoft.Extensions.Logging; // Example using Microsoft.Extensions.Logging
106+
107+
namespace CompanyBetagro.Authentication
108+
{
109+
// Using interfaces for dependencies (Dependency Inversion)
110+
public interface IUserDatabase
111+
{
112+
User GetUser(string username);
113+
}
114+
115+
public class User
116+
{
117+
public string Username { get; }
118+
public string PasswordHash { get; } // Stored hash includes salt and iterations
119+
120+
public User(string username, string passwordHash)
121+
{
122+
Username = username ?? throw new ArgumentNullException(nameof(username));
123+
PasswordHash = passwordHash ?? throw new ArgumentNullException(nameof(passwordHash));
124+
}
125+
}
126+
127+
/// <summary>
128+
/// Provides services for user authentication.
129+
/// </summary>
130+
public class UserAuthenticationService
131+
{
132+
// Use modern password hashing like PBKDF2
133+
private const int SaltSize = 16; // 128 bit
134+
private const int HashSize = 32; // 256 bit
135+
private const int Iterations = 10000; // Number of iterations
136+
137+
private readonly ILogger<UserAuthenticationService> _logger;
138+
private readonly IUserDatabase _userDatabase;
139+
140+
/// <summary>
141+
/// Initializes a new instance of the <see cref="UserAuthenticationService"/> class.
142+
/// </summary>
143+
/// <param name="logger">The logger instance.</param>
144+
/// <param name="userDatabase">The user database dependency.</param>
145+
public UserAuthenticationService(ILogger<UserAuthenticationService> logger, IUserDatabase userDatabase)
146+
{
147+
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
148+
_userDatabase = userDatabase ?? throw new ArgumentNullException(nameof(userDatabase));
149+
}
150+
151+
/// <summary>
152+
/// Hashes a password using PBKDF2 with a random salt.
153+
/// </summary>
154+
/// <param name="password">The password to hash.</param>
155+
/// <returns>A Base64 encoded string containing salt, iterations, and hash.</returns>
156+
/// <exception cref="ArgumentNullException">Thrown if password is null.</exception>
157+
public string HashPassword(string password)
158+
{
159+
if (password == null) throw new ArgumentNullException(nameof(password));
160+
161+
byte[] salt = RandomNumberGenerator.GetBytes(SaltSize);
162+
163+
using (var pbkdf2 = new Rfc2898DeriveBytes(password, salt, Iterations, HashAlgorithmName.SHA256))
164+
{
165+
byte[] hash = pbkdf2.GetBytes(HashSize);
166+
167+
// Combine salt, iterations, and hash for storage
168+
byte[] iterationBytes = BitConverter.GetBytes(Iterations);
169+
byte[] combinedBytes = new byte[SaltSize + sizeof(int) + HashSize];
170+
171+
Buffer.BlockCopy(salt, 0, combinedBytes, 0, SaltSize);
172+
Buffer.BlockCopy(iterationBytes, 0, combinedBytes, SaltSize, sizeof(int));
173+
Buffer.BlockCopy(hash, 0, combinedBytes, SaltSize + sizeof(int), HashSize);
174+
175+
return Convert.ToBase64String(combinedBytes);
176+
}
177+
}
178+
179+
/// <summary>
180+
/// Authenticates a user against the database by verifying the password.
181+
/// </summary>
182+
/// <param name="username">The user's username.</param>
183+
/// <param name="password">The user's password.</param>
184+
/// <returns>True if the user is authenticated, False otherwise.</returns>
185+
public bool AuthenticateUser(string username, string password)
186+
{
187+
if (string.IsNullOrWhiteSpace(username) || password == null)
188+
{
189+
return false; // Or throw ArgumentException based on policy
190+
}
191+
192+
try
193+
{
194+
User user = _userDatabase.GetUser(username);
195+
if (user == null)
196+
{
197+
_logger.LogWarning("Authentication failed: User not found - {Username}", username);
198+
return false;
199+
}
200+
201+
// Extract salt, iterations, and hash from stored value
202+
byte[] combinedBytes = Convert.FromBase64String(user.PasswordHash);
203+
if (combinedBytes.Length != SaltSize + sizeof(int) + HashSize)
204+
{
205+
_logger.LogError("Invalid password hash format for user - {Username}", username);
206+
return false;
207+
}
208+
209+
byte[] salt = new byte[SaltSize];
210+
byte[] iterationBytes = new byte[sizeof(int)];
211+
byte[] storedHash = new byte[HashSize];
212+
213+
Buffer.BlockCopy(combinedBytes, 0, salt, 0, SaltSize);
214+
Buffer.BlockCopy(combinedBytes, SaltSize, iterationBytes, 0, sizeof(int));
215+
Buffer.BlockCopy(combinedBytes, SaltSize + sizeof(int), storedHash, 0, HashSize);
216+
217+
int iterations = BitConverter.ToInt32(iterationBytes, 0);
218+
219+
// Hash the provided password with the stored salt and iterations
220+
using (var pbkdf2 = new Rfc2898DeriveBytes(password, salt, iterations, HashAlgorithmName.SHA256))
221+
{
222+
byte[] calculatedHash = pbkdf2.GetBytes(HashSize);
223+
224+
// Compare the hashes securely
225+
if (CryptographicOperations.FixedTimeEquals(calculatedHash, storedHash))
226+
{
227+
_logger.LogInformation("User authenticated successfully - {Username}", username);
228+
return true;
229+
}
230+
else
231+
{
232+
_logger.LogWarning("Authentication failed: Incorrect password - {Username}", username);
233+
return false;
234+
}
235+
}
236+
}
237+
catch (FormatException ex)
238+
{
239+
_logger.LogError(ex, "Error parsing password hash for user - {Username}", username);
240+
return false;
241+
}
242+
catch (Exception ex) // Catch broader exceptions for unexpected errors during auth
243+
{
244+
_logger.LogError(ex, "An unexpected error occurred during authentication for {Username}", username);
245+
return false; // Fail secure
246+
}
247+
}
248+
}
249+
}

0 commit comments

Comments
 (0)