I can't speak for the developers of lavaan, but I would like to share my view from developing a package.
The function lavaan(), I believe, is intended to be the main (final) function for model fitting. That is, it is the "real" function doing this job. The "final stop" before starting the SEM analysis. It also has some default values for some arguments (like many R functions).
However, there are cases in which some default values may not be appropriate for some models. It would be inconvenient to keep changing these default values every time we fit those models. Therefore, some "wrappers" were developed, such as sem(), cfa(), and growth(). These wrappers have default values for some arguments that are different from lavaan() (e.g., see the help page of
https://rdrr.io/cran/lavaan/man/sem.html). They are useful because we don't have to keep changing them when calling lavaan(), and can simplify things because things that we need to do manually by the model syntax for these models when using lavaan() directly are then handled automatically by those default values of the warppers.
The lavaan() function may or may not be intended for everyday use when it was first written. I am not sure because I didn't use lavaan when it was first released. However, for the last few years, I have never encountered cases in which I need to teach students to use this function. The wrappers are intended to prevent users from making mistakes when fitting common models, such as forgetting to add something they should. I didn't mention this function to prevent any confusion. If necessary, I would tell my students not to use it directly, unless they know what they are doing.
Default behaviors are present for many SEM programs. For example, in AMOS, when we draw a diagram, covariances between exogenous variables are not included automatically. Users have to add them. (Missing those covariances seems to be quite common for new learners of AMOS, in my experience.)
It does not mean there is something wrong with lavaan() or its default values. We just can't, and don't have to, make a function work for all possible scenarios (models). Practically, we just set what are common, but then write wrappers when necessary.
I oversimplified the idea (and I have no formal training as a programmer and so I may be wrong on some aspects; please correct me if I made any mistakes about the practice and concept). Wrappers are common in programming, and they sometimes do more than just set the default values. They may preprocess the input objects before calling the main functions and/or postprocess the output objects returned by the main functions:
https://en.wikipedia.org/wiki/Wrapper_functionMany common R functions are wrappers. For example, sapply() is a wrapper of lapply (and a simple one; type sapply to see the source code), and replicate() is a wrapper of sapply(). The function log10() is a wrapper of log(). The function for ANOVA, aov(), is also a wrapper of lm(), the function to do regression.
My personal suggestion: If you are new to lavaan and not sure what to use, simply don't use lavaan() directly, until you really believe you have to.
(Jeremy may disagree, and I am happy to learn about different opinions. :>)
There are cases in which lavaan() can or should be used directly. For example, when refitting a model in exactly the way we want it, when we only have the fitted object (the output of lavaan()) and not the model syntax.
However, those cases are usually in developing things to work with lavaan, not for fitting models in research.
My two cents.
-- Shu Fai
P.S.: A technical note. Despite what the help pages of sem() and cfa() say, internally, they no longer just set the values for some arguments and then call lavaan(), as they were in the past. However, they are still wrappers.