Functional Programming principles you can apply in any codebase, any language, right now, without complicated concepts or words

Some core concepts of functional programming, and why you should care.

Purity over Side Effects

A pure function does nothing but return a value based on its arguments. This is a simple concept, but brings many positive consequences: no "hidden" dependencies, code that is easier to test, more predictable outcomes and so on. Of course, at some stage you will need to introduce side effects (ie. getting user input, reading from a file, saving to a database) to make your program useful. A functional programmer will try to isolate those to the "edges" of the application, ensuring the core logic is made entirely of pure functions and restricting side effects to a specific layer of the code.

Transformation over Mutation

Instead of creating variables and mutating them over time, try to work with constant, immutable data structures. Pass those to pure functions that return a new, slightly different version of the structure instead of directly mutating it. It's a subtle difference that has a drastic impact on the way your code works, not only helping with strange bugs due to unexpected mutations along the call stack but also making concurrency easier.

Errors as Data over Exceptions

As the name implies, exceptions should be exceptional - unexpected errors that should never happen. However, using exceptions for flow control is a widespread practice that makes code harder to understand and follow - if you ever spent time trying to figure out which part of the stack was catching a specific exception, you'll know what I mean. If a function can fail for a known reason, that should be represented by its return value. Languages like Haskell offer specific data types for this, like Maybe or Either; languages without static types usually do this with a tuple - Elixir functions, for example, often return {:ok, value} or {:error, description}.

Completeness over Partial Implementations

When pattern-matching or using other forms of conditional logic, always aim for completeness - that is, covering all possible cases. Most languages will happily compile and run partial implementations, which lead to runtime errors (at best) or erratic behavior (at worst). This is particularly valuable when combined with the "Errors as Data" approach - if your return value may be an error and you're going for completeness, you are compelled to handle the error case.

Function Composition over Predefined Patterns

While object-oriented programming relies on using patterns (ie. Gang of Four) to solve common problems, functional programming encourages writing small, expressive, pure functions that can be combined (composed) in different ways and reused across your application (and beyond). The fact these functions are pure gives you the confidence that reusing them in different situations won't cause any undesired side effects, and again makes reasoning about the code easier.

Language features like higher-order functions and closures are extremely powerful and can completely replace entire collections of classes in typical OOP patterns; more advanced category theory concepts (like Monoids, Applicatives and Monads) have scary names but are essentially ways to combine, reuse and sequence your functions across different contexts.

Explicit over Implicit

Reasoning about a piece of code is always easier when that piece of code is explicit and as self-contained as possible. The more layers of indirection are added, the harder is becomes to comprehend the system as a whole. All of the points above contribute to a system that is more explicit - read them again and try to understand how this happens, and why it makes a codebase easier to grasp. Eventually, the FP light bulb in your brain will flick on - you'll just "get it", and explicitness will become one of the guiding principles when writing any piece of software, in any language, in any paradigm.