There are of course cases of dynamic data in every language (The table name is an apt example) but usually when I look in code I just expect to be able to follow definitions. If the language doesn't reliably allow me to find "usages of this type" without risking finding another type with the exact same name then I'm already starting up my static type system compiler for the rewrite.
There are exceptions of course: when searching git logs, comments etc doesn't help what the language or IDE does.
And when searching for an unknown symbol (type, function, variable) you don't know the name of, but you know _should_ look like "DogOrder" or "OrderDog" is a common task too. In this case I'd probably search for " Dog.Order\(" or " Order.Dog\(" if I'm looking for a function. The language trait that enabled it is that method names are Pascal Case and always have an opening ( at the end. But my IDE at least lets me search for members (variables, functions) separate from type names. There should be an index in the IDE though that lets you query this data. E.g. looking for types starting with foo could be done with search t:Foo, instead of having to grep for "(struct|class) Foo" or similar. Tooling is the key.
The author uses JavaScript and Python as examples. So I presume they have (most?) experience with dynamic languages.
In static languages, greppability is hardly as much as a factor. Especially with the availability of LSPs and other such tools nowadays.
When I write rust, or Java, I hardly grep, I "go to usages" or "go to definition", "rename symbol" and so on. Similar, but not to that extent, with typescript. But when coding in Javascript, Ruby or Python, no matter how fancy or language-focused an IDE is, I'll be grepping a lot. Decades of Ruby and Rails "black magic" taught me to grep for partial patterns like the author shows, too. Or to just run the code-path entirely (through tests) because the table-definition of the database will change the available methods and behaviour of the code. Yes. I know.
An LSP (or linter, or checker) can only do so much when the available code, methods, classes, behaviour can be changed or added at runtime.
Then again, in languages like Java that go all-in on object orientation and dynamic dispatch, "go to definition" can become a real chore in sufficiently large codebases. I must have wasted hours of my life trying to find which class is the one implementing some interface or abstract method at a certain point. Bonus points if the implementing class changes based on ordinary runtime conditions.
I'm happy to use dynamic languages occasionally too (Bash, Javascript, Python, ..) but I have a rule of thumb that says if I can't see the entire codebase on one screen, then it's too large for dynamic.
There are exceptions of course: when searching git logs, comments etc doesn't help what the language or IDE does.
And when searching for an unknown symbol (type, function, variable) you don't know the name of, but you know _should_ look like "DogOrder" or "OrderDog" is a common task too. In this case I'd probably search for " Dog.Order\(" or " Order.Dog\(" if I'm looking for a function. The language trait that enabled it is that method names are Pascal Case and always have an opening ( at the end. But my IDE at least lets me search for members (variables, functions) separate from type names. There should be an index in the IDE though that lets you query this data. E.g. looking for types starting with foo could be done with search t:Foo, instead of having to grep for "(struct|class) Foo" or similar. Tooling is the key.