День из жизни C# разработчика: код, кофе и магия .NET

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Вступление: Легенда о «мёртвом» C#

Вы наверняка слышали, что C# — это язык для «корпоратов», где всё скучно, медленно и покрыто слоем энтерпрайз-бюрократии. Но правда в том, что C# разработчик сегодня — это универсальный солдат IT. Он пишет быстрые микросервисы на ASP.NET Core, оптимизирует запросы к БД через Entity Framework, а иногда даже встраивает машинное обучение с ML.NET.

В этой статье я не буду рассказывать абстрактные теории. Мы пройдём типичный рабочий день C# программиста в реальной компании: от утреннего кофе до пуша кода в прод. Вы увидите примеры кода, инструменты и лайфхаки, которые сэкономят часы работы.

08:30 — Утро: Кофе, почта и доска задач

День начинается не с кода, а с контекста. Первым делом я открываю Slack и Jira. В типичной команде есть утренний стендап (daily stand-up). На нём мы отвечаем на три вопроса:

  • Что сделано вчера?
  • Что буду делать сегодня?
  • Есть ли блокеры?

После стендапа — беглый просмотр пул-реквестов коллег. Код-ревью на C# — это не просто «галочка». Здесь важно проверить, нет ли утечки памяти (особенно с IDisposable) и правильно ли используется async/await.

Пример из жизни: код-ревью асинхронного метода

// Плохо: блокирующий вызов внутри async метода
public async Task<User> GetUserAsync(int id)
{
    var user = _context.Users.Find(id); // .Find — синхронный!
    return await Task.FromResult(user); // Бесполезная обёртка
}

// Хорошо: полностью асинхронно
public async Task<User> GetUserAsync(int id)
{
    return await _context.Users.FindAsync(id); // Реальная асинхронность
}

Такая мелочь может убить производительность под нагрузкой. Поэтому C# программист обязан следить за асинхронностью на каждом шагу.

10:00 — Работа с задачей: Пишем новый API endpoint

Сегодня в бэклоге — задача «Добавить фильтрацию товаров по цене». Это типичная история для .NET разработчика. Я работаю в Visual Studio 2022 с ReSharper (да, он тормозит, но без него как без рук).

Шаг 1: Модель и DTO

Сначала определяем модель данных. Допустим, у нас есть Entity Framework Core и база PostgreSQL.

public class Product
{
    public int Id { get; set; }
    public string Name { get; set; }
    public decimal Price { get; set; }
    public string Category { get; set; }
}

public class ProductFilterDto
{
    public decimal? MinPrice { get; set; }
    public decimal? MaxPrice { get; set; }
    public string? Category { get; set; }
}

Шаг 2: Репозиторий с IQueryable

Главный трюк — не тащить все данные в память, а использовать IQueryable. Это позволяет сформировать SQL-запрос на стороне базы.

public async Task<List<Product>> GetFilteredProductsAsync(ProductFilterDto filter)
{
    var query = _context.Products.AsQueryable();

    if (filter.MinPrice.HasValue)
        query = query.Where(p => p.Price >= filter.MinPrice.Value);

    if (filter.MaxPrice.HasValue)
        query = query.Where(p => p.Price <= filter.MaxPrice.Value);

    if (!string.IsNullOrEmpty(filter.Category))
        query = query.Where(p => p.Category == filter.Category);

    return await query.ToListAsync();
}

Обратите внимание: мы не вызываем .ToList() до самого конца. Это базовый, но критически важный паттерн для любого C# разработчика.

Шаг 3: Контроллер ASP.NET Core

[ApiController]
[Route("api/[controller]")]
public class ProductsController : ControllerBase
{
    private readonly IProductRepository _repo;

    public ProductsController(IProductRepository repo) => _repo = repo;

    [HttpGet("filter")]
    public async Task<IActionResult> GetFiltered([FromQuery] ProductFilterDto filter)
    {
        var products = await _repo.GetFilteredProductsAsync(filter);
        return Ok(products);
    }
}

Весь процесс занял около 40 минут. Но рабочий день программиста не состоит только из написания кода.

11:30 — Мид-дей: Баги, логи и дебаг

Прилетает баг-репорт: «При загрузке файла больше 10 MB падает с ошибкой 413». Это классика для C# бэкенд разработчика.

Первым делом я смотрю логи в Serilog (или Application Insights). Нахожу ошибку:

System.IO.InvalidDataException: Multipart body length limit 10485760 exceeded.

Решение: увеличить лимит в Program.cs:

builder.Services.Configure<FormOptions>(options =>
{
    options.MultipartBodyLengthLimit = 50 * 1024 * 1024; // 50 MB
});

Но просто увеличить лимит — плохая практика. Нужно добавить проверку размера на клиенте и в middleware. Для этого пишем кастомный атрибут:

[AttributeUsage(AttributeTargets.Method)]
public class RequestSizeLimitAttribute : Attribute
{
    public long SizeLimit { get; }

    public RequestSizeLimitAttribute(long sizeInBytes) => SizeLimit = sizeInBytes;
}

Исправление бага заняло 20 минут. Ещё 10 — на написание юнит-теста (xUnit + Moq).

13:00 — Обед и рефлексия

Обед — это святое. В перерыве я читаю Stack Overflow или смотрю новые фичи C# 12. Например, primary constructors:

public class ProductService(IProductRepository repo) : IProductService
{
    // repo уже доступен без отдельного поля
    public async Task<Product> GetByIdAsync(int id) => await repo.GetByIdAsync(id);
}

Это упрощает код и уменьшает бойлерплейт. C# разработчик должен следить за обновлениями языка, чтобы не писать как в 2015 году.

14:30 — Вторая половина дня: Интеграция и тесты

После обеда — работа с внешним API. Нужно интегрировать сервис доставки. Использую HttpClientFactory (никогда не создавайте новый HttpClient вручную — это утечка сокетов).

Пример: типичная интеграция

public class DeliveryService : IDeliveryService
{
    private readonly HttpClient _httpClient;

    public DeliveryService(IHttpClientFactory factory)
    {
        _httpClient = factory.CreateClient("DeliveryApi");
    }                

Блоги

Рекомендаций по книгам