There is often a lot of confusion about what "declarative" means in the context of code. As someone building a framework that I would call "declarative", I've had to think about this quite a lot.
What I've ultimately landed on is that declarative is typically a relative term, related to just how specific you have to get to tell the machine what to do. It is relative to "how much you usually have to tell it".
SQL is the best example of declarative design. You don't provide instructions on how to traverse the table, or where to store things in memory, you just tell it "I want these fields from this table". A planner figures out how to do that for you, and it does it exactly 1 gajillion times better than you would ever hand-write.
SELECT * FROM users WHERE splines = "reticulated"
You could write this yourself, but you would:
1. not enjoy it
2. almost certainly have a bug in your code somewhere
3. miss out on the likely 40+ years of optimization and tooling built directly into your SQL database of choice
page = find_start_page("users")
cols = table_cols("users")
for column <- cols:
I'm already frustrated even trying to write barely equivalent pseudocode.
## Declarative *Design*
There isn't much of a difference between declarative and imperative *code* (they are both just instructions). This is why I use the phrase Declarative Design. The things that are declarative come from the design of your application, i.e your interfaces, configurations and interaction points.
> All declarative code is backed by imperative code somewhere else. The main difference is that the imperative code often does difficult or non-obvious things on your behalf.
The point of declarative design is to separate the "what" from the "how", but you *still need both*! The point is not to magic away the "how", but to allow it to grow independently of the "what". There are various benefits to this pattern, which I expand on in my other article, [[An Incremental Approach to Declarative Design]]().