DEV Community

Lucas Pereira de Souza
Lucas Pereira de Souza

Posted on

Shadcn/ui e a morte das UI Libs tradicionais

logotech

## Proprietary Components: A Revolution in Code Ownership and Development

In the fast-paced world of software development, we often find ourselves grappling with a common challenge: managing dependencies and ensuring the long-term maintainability of our projects. The rise of open-source has brought immense benefits, but it has also led to a complex ecosystem of third-party libraries, frameworks, and components. While these external pieces of code offer convenience and accelerate development, they can also introduce risks related to security, stability, and vendor lock-in. This is where the concept of \"proprietary components\" or \"owning your code\" emerges as a powerful paradigm shift.

The Problem with the \"Black Box\" Approach

Traditionally, many projects have relied heavily on external, often opaque, components. Developers consume these components as \"black boxes,\" trusting that they function as intended without fully understanding their inner workings. This approach, while quick to implement initially, can lead to several pitfalls:

  • Security Vulnerabilities: A vulnerability in a third-party dependency can have a cascading effect, compromising your entire application. Patching these issues often depends on the original maintainers, leading to delays and potential exposure.
  • Lack of Customization: When a component doesn't quite fit your specific needs, you're often limited by the choices of its creators. This can lead to workarounds, hacks, or the painful decision to refactor significant portions of your application.
  • \"Dependency Hell\": As projects grow, managing multiple dependencies and their versions can become a nightmare. Incompatibilities, transitive dependencies, and breaking changes can halt development progress.
  • Vendor Lock-in: Over-reliance on a specific proprietary component or platform can make it incredibly difficult and costly to migrate to a different solution later on.
  • Hidden Costs: While often free, open-source components can incur significant indirect costs in terms of debugging, security audits, and the time spent understanding their intricacies when issues arise.

Embracing Code Ownership: The \"Proprietary Components\" Philosophy

The \"proprietary components\" approach, in this context, doesn't necessarily mean reinventing the wheel for every single piece of functionality. Instead, it advocates for a strategic shift towards building and owning the core components that are critical to your application's unique value proposition and long-term success. This means:

  1. Understanding the Core: Identify the key functionalities that differentiate your product. These are the areas where you should aim for deep understanding and control.
  2. Internalizing Critical Logic: For these core functionalities, consider building them in-house or significantly customizing and extending existing open-source solutions to the point where you truly \"own\" the logic.
  3. Strategic Dependency Management: For non-core functionalities (e.g., logging, basic utility functions), leverage well-vetted open-source libraries. However, even here, be mindful of the potential risks and have a strategy for managing them.
  4. Focus on Maintainability and Testability: When building your own components, prioritize clean code, strong typing, and comprehensive testing from the outset.

Demonstrating with Code: A TypeScript/Node.js Example

Let's illustrate this with a simplified example. Imagine we're building an e-commerce platform and need a robust PricingCalculator component. Instead of relying solely on an external library that might not perfectly match our complex discount rules, we decide to build our own.

// src/core/pricing/PricingCalculator.ts

/**
 * Represents an item in an order, including its price and quantity.
 */
interface OrderItem {
  productId: string;
  quantity: number;
  unitPrice: number;
}

/**
 * Represents a discount that can be applied to an order.
 */
interface Discount {
  code: string;
  percentage: number; // e.g., 0.10 for 10%
}

/**
 * Calculates pricing for orders, including discounts and taxes.
 * This is a core component of our e-commerce platform.
 */
class PricingCalculator {
  private taxRate: number;

  /**
   * Initializes the PricingCalculator with a specific tax rate.
   * @param taxRate - The applicable tax rate (e.g., 0.07 for 7%).
   */
  constructor(taxRate: number) {
    if (taxRate < 0 || taxRate > 1) {
      throw new Error(\"Tax rate must be between 0 and 1.\");
    }
    this.taxRate = taxRate;
  }

  /**
   * Calculates the subtotal of an order based on the items and their quantities.
   * @param items - An array of OrderItem objects.
   * @returns The calculated subtotal.
   */
  calculateSubtotal(items: OrderItem[]): number {
    // Using reduce for efficient summation. Clear variable names enhance readability.
    const subtotal = items.reduce((sum, item) => {
      // Basic validation to prevent negative quantities or prices
      if (item.quantity < 0 || item.unitPrice < 0) {
        console.warn(`Invalid item data detected for productId: ${item.productId}. Skipping.`);
        return sum;
      }
      return sum + (item.quantity * item.unitPrice);
    }, 0);

    return parseFloat(subtotal.toFixed(2)); // Ensure currency formatting
  }

  /**
   * Applies discounts to a given amount.
   * @param amount - The base amount to apply discounts to.
   * @param discounts - An array of Discount objects.
   * @returns The amount after applying all applicable discounts.
   */
  applyDiscounts(amount: number, discounts: Discount[]): number {
    let discountedAmount = amount;

    // Iterate through discounts and apply them sequentially.
    for (const discount of discounts) {
      // Validate discount percentage
      if (discount.percentage < 0 || discount.percentage > 1) {
        console.warn(`Invalid discount percentage for code: ${discount.code}. Skipping.`);
        continue;
      }
      // Apply discount: amount * (1 - percentage)
      discountedAmount *= (1 - discount.percentage);
    }

    return parseFloat(discountedAmount.toFixed(2));
  }

  /**
   * Calculates the total price of an order, including subtotal, discounts, and taxes.
   * @param items - An array of OrderItem objects.
   * @param discounts - An array of Discount objects to apply.
   * @returns The final calculated total price.
   */
  calculateTotalPrice(items: OrderItem[], discounts: Discount[]): number {
    const subtotal = this.calculateSubtotal(items);
    const amountAfterDiscounts = this.applyDiscounts(subtotal, discounts);

    // Calculate tax: amount * taxRate
    const taxAmount = amountAfterDiscounts * this.taxRate;

    // Final total: amount after discounts + tax
    const totalPrice = amountAfterDiscounts + taxAmount;

    return parseFloat(totalPrice.toFixed(2));
  }
}

// --- Example Usage ---

// Instantiate the calculator with a 7% tax rate
const calculator = new PricingCalculator(0.07);

// Define order items
const orderItems: OrderItem[] = [
  { productId: 'prod-123', quantity: 2, unitPrice: 25.50 },
  { productId: 'prod-456', quantity: 1, unitPrice: 75.00 },
  { productId: 'prod-789', quantity: 3, unitPrice: 10.00 },
];

// Define applicable discounts
const orderDiscounts: Discount[] = [
  { code: 'SUMMER10', percentage: 0.10 }, // 10% off
  { code: 'LOYALTY5', percentage: 0.05 }, // 5% off
];

// Calculate and log the results
const subtotal = calculator.calculateSubtotal(orderItems);
console.log(`Subtotal: $${subtotal}`); // Expected: $176.00

const finalPrice = calculator.calculateTotalPrice(orderItems, orderDiscounts);
console.log(`Final Price (after discounts and tax): $${finalPrice}`);
// Expected calculation:
// Subtotal: 176.00
// After 10% discount: 176.00 * 0.90 = 158.40
// After 5% discount: 158.40 * 0.95 = 150.48
// Tax (7%): 150.48 * 0.07 = 10.5336
// Total: 150.48 + 10.5336 = 161.0136 -> Rounded to 161.01

export { PricingCalculator, OrderItem, Discount }; // Exporting for potential use in other modules
Enter fullscreen mode Exit fullscreen mode

In this example:

  • Strong Typing: We use TypeScript interfaces (OrderItem, Discount) and class types (PricingCalculator) to define clear contracts and ensure data integrity.
  • Clean Code Principles: Method and variable names are descriptive (calculateSubtotal, unitPrice). The logic is broken down into smaller, testable methods.
  • Error Handling & Validation: Basic checks are included for invalid inputs (e.g., negative quantities, invalid tax rates), preventing unexpected behavior.
  • Comments: Explanations are provided for complex parts or design decisions, such as the sequential application of discounts or the rounding for currency.
  • Modularity: The PricingCalculator is a self-contained unit, making it easier to test, reuse, and refactor independently.

Conclusion: Building for the Future

Adopting a \"proprietary components" philosophy is not about resisting external libraries; it's about making informed decisions regarding where to invest development effort and gain control. By owning the critical pieces of your software, you gain:

  • Enhanced Security: You control the codebase and can respond rapidly to vulnerabilities.
  • Greater Flexibility: You can tailor components precisely to your business logic and adapt them as your needs evolve.
  • Reduced Risk: You minimize reliance on external factors and mitigate the impact of third-party issues.
  • Long-Term Maintainability: Well-designed, owned components are easier to understand, debug, and evolve over time.

This strategic approach allows teams to build more resilient, adaptable, and ultimately more valuable software. It's an investment in the future stability and success of your product.

Top comments (0)