I don't know if you can find any valuable resources or articles about the topic, but I think that the idea of the textual description is good.
Personally, I have some experience with vertical-scroller shmups, and I have always followed this approach.
Roughly speaking, you can have a series of text files (or other kind of file...); each file corresponds to a level of your game.
Every row of a file contains the essential informations that you need for generating the enemies (or other objects, such as background elements - like planets, stars,...).
These informations, obviously, vary accordingly with your specific game but, in general, they could be:
The spawn time: this is the time that you have to wait between a "row" and another. Basically, you control the frequency of spawning, with this "parameter";
The enemy type: this could be an index, or a string, that represents the specific enemy type. As an example, if you have 20 types of enemies in your game, this can be
a numerical index, from 1 to 20, that represents the type of object to be instantiated: a particular function in your code will bind the index with the specific asset of
the enemy. As I said, you could also use the string that represents the asset's name in your project: It will just be a unique identifier for the object to be instantiated.
The x and y coordinates that define the spawn point (y-coordinate could be avoided, and defined as a constant, if all the enemies come from the top of the screen).
So, your txt file can be structured in a similar fashion:
4.0 little_enemy_1 200 150
0.0 little enemy_1 240 150
0.0 little_enemy_1 280 150
3.5 mothership 140 170
In Unity (sorry, but I'm familiar with that...) these lines correspond to:
yield WaitForSeconds(4);
Instantiate (little_enemy_1, Vector3.Position(200, 150, 0), Quaternion.identity);
Instantiate (little_enemy_1, Vector3.Position(240, 150, 0), Quaternion.identity);
Instantiate (little_enemy_1, Vector3.Position(280, 150, 0), Quaternion.identity);
yield WaitForSeconds(3.5);
Instantiate (mothership, Vector3.Position(140, 170, 0), Quaternion.identity);
If you're not familiar with Unity, those lines say: "wait 4 seconds, generate 3 little enemies in a row, wait 3.5 seconds, and generate a mothership (all in the proper coordinates)".
In my example I have not considered the particular rotation of the objects that have to be spawned, but, obviously, you can also add that.
I know that it was just a quick tip, but I hope that it could represent a good starting point for you, as you asked. Any feedback will be appreciated, of course.